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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7135d1659f6d6564addc0f472fbf72a6225098a29a9de3e9a3c16af4724b364e
4
+ data.tar.gz: fdfe10e7e4e59e7bfb4b4c08f05dc2a8190acc36c2bd5602277c2298a2f4072e
5
+ SHA512:
6
+ metadata.gz: 294e2feacac1639da7c4b927fd9580f8c5e000bbd93885893a1c647cd980327ff8dd6d01d83db044cfc56da5dc71f2c62825deb07f71f85e042eeb59a54babb9
7
+ data.tar.gz: 014a4986c50d89dad1f932d8506d3171c776c91aedf83e959d6bab7e9a0d365255d31ce99aa960229189a959c1e744bb2c2ba889f46863677dd3abe4acfb2bb0
data/.editorconfig ADDED
@@ -0,0 +1,20 @@
1
+ # editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ charset = utf-8
6
+ indent_style = space
7
+ end_of_line = lf
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [{*.rb,*.haml,*.decorator,*.yml,*.yaml,*.jbuilder}]
12
+ indent_size = 2
13
+ indent_style = space
14
+
15
+ [{*.js,*.jst,*.ejs,*.scss}]
16
+ indent_size = 4
17
+
18
+ [*.md]
19
+ indent_size = 4
20
+ trim_trailing_whitespace = false
data/.eslintignore ADDED
@@ -0,0 +1,2 @@
1
+ **/test/**/*.js
2
+ testing/**/spec_helper.js
data/.eslintrc ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "extends": "eslint:recommended",
3
+ "rules": {
4
+ "semi": [1, "always"]
5
+ },
6
+ "globals": {
7
+ "window": true,
8
+ "document": true,
9
+ "WORKAREA": true,
10
+ "quot;: true,
11
+ "jQuery": true,
12
+ "_": true,
13
+ "feature": true,
14
+ "JST": true,
15
+ "Turbolinks": true,
16
+ "I18n": true,
17
+ "Chart": true,
18
+ "Dropzone": true,
19
+ "strftime": true,
20
+ "Waypoint": true,
21
+ "wysihtml": true,
22
+ "LocalTime": true,
23
+ }
24
+ }
data/.eslintrc.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "extends": "eslint:recommended",
3
+ "rules": {
4
+ "semi": ["error", "always"],
5
+ "eqeqeq": ["error", "always"]
6
+ },
7
+ "globals": {
8
+ "window": true,
9
+ "document": true,
10
+ "WORKAREA": true,
11
+ "quot;: true,
12
+ "jQuery": true,
13
+ "_": true,
14
+ "feature": true,
15
+ "JST": true,
16
+ "Turbolinks": true,
17
+ "I18n": true,
18
+ "Chart": true,
19
+ "Dropzone": true,
20
+ "strftime": true,
21
+ "Waypoint": true,
22
+ "wysihtml": true,
23
+ "LocalTime": true,
24
+ "describe": true,
25
+ "after": true,
26
+ "afterEach": true,
27
+ "before": true,
28
+ "beforeEach": true,
29
+ "it": true,
30
+ "expect": true,
31
+ "sinon": true,
32
+ "fixture": true,
33
+ "chai": true
34
+ }
35
+ }
data/.github/ISSUE_TEMPLATE/bug_report.md ADDED
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve Workarea
4
+ title: ''
5
+ labels: bug
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ⚠️**Before you create**⚠️
11
+ Please verify the issue you're experiencing is not part of your Workarea project customizations. The best way to do this is with a [vanilla Workarea installation](https://developer.workarea.com/articles/create-a-new-host-application.html). This will help us spend time on fixes/improvements for the whole community. Thank you!
12
+
13
+ **Describe the bug**
14
+ A clear and concise description of what the bug is.
15
+
16
+ **To Reproduce**
17
+ Steps to reproduce the behavior:
18
+ 1. Go to '...'
19
+ 2. Click on '....'
20
+ 3. Scroll down to '....'
21
+ 4. See error
22
+
23
+ **Expected behavior**
24
+ A clear and concise description of what you expected to happen.
25
+
26
+ **Workarea Setup (please complete the following information):**
27
+ - Workarea Version: [e.g. v3.4.6]
28
+ - Plugins [e.g. workarea-blog, workarea-sitemaps]
29
+
30
+ **Attachments**
31
+ If applicable, add any attachments to help explain your problem, things like:
32
+ - screenshots
33
+ - Gemfile.lock
34
+ - test cases
35
+
36
+ **Additional context**
37
+ Add any other context about the problem here.
data/.github/ISSUE_TEMPLATE/documentation-request.md ADDED
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: Documentation request
3
+ about: Suggest documentation
4
+ title: ''
5
+ labels: documentation
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your documentation related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm confused by [...]
12
+
13
+ **Describe the article you'd like**
14
+ A clear and concise description of what would be in the documentation article.
15
+
16
+ **Additional context**
17
+ Add any other context or screenshots about the feature request here.
data/.github/ISSUE_TEMPLATE/feature_request.md ADDED
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for Workarea
4
+ title: ''
5
+ labels: enhancement
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
data/.github/workflows/ci.yml ADDED
@@ -0,0 +1,60 @@
1
+ name: CI
2
+ on: [push]
3
+
4
+ jobs:
5
+ static_analysis:
6
+ runs-on: ubuntu-latest
7
+ steps:
8
+ - uses: actions/checkout@v1
9
+ - uses: workarea-commerce/ci/bundler-audit@v1
10
+ - uses: workarea-commerce/ci/rubocop@v1
11
+ - uses: workarea-commerce/ci/eslint@v1
12
+ with:
13
+ args: '**/*.js'
14
+ - uses: workarea-commerce/ci/stylelint@v1
15
+ with:
16
+ args: '**/*.scss'
17
+
18
+ admin_tests:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v1
22
+ - uses: actions/setup-ruby@v1
23
+ with:
24
+ ruby-version: 2.6.x
25
+ - uses: workarea-commerce/ci/test@v1
26
+ with:
27
+ command: bin/rails app:workarea:test:admin
28
+
29
+ core_tests:
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v1
33
+ - uses: actions/setup-ruby@v1
34
+ with:
35
+ ruby-version: 2.6.x
36
+ - uses: workarea-commerce/ci/test@v1
37
+ with:
38
+ command: bin/rails app:workarea:test:core
39
+
40
+ storefront_tests:
41
+ runs-on: ubuntu-latest
42
+ steps:
43
+ - uses: actions/checkout@v1
44
+ - uses: actions/setup-ruby@v1
45
+ with:
46
+ ruby-version: 2.6.x
47
+ - uses: workarea-commerce/ci/test@v1
48
+ with:
49
+ command: bin/rails app:workarea:test:storefront
50
+
51
+ plugins_tests:
52
+ runs-on: ubuntu-latest
53
+ steps:
54
+ - uses: actions/checkout@v1
55
+ - uses: actions/setup-ruby@v1
56
+ with:
57
+ ruby-version: 2.6.x
58
+ - uses: workarea-commerce/ci/test@v1
59
+ with:
60
+ command: bin/rails app:workarea:test:plugins
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ .DS_Store
2
+ .bundle/
3
+ .byebug_history
4
+ Gemfile.lock
5
+ .sass-cache/
6
+ log/*.log
7
+ pkg/
8
+ test/dummy/db/*.sqlite3
9
+ test/dummy/db/*.sqlite3-journal
10
+ test/dummy/log/*.log
11
+ test/dummy/tmp/
12
+ test/dummy/public/
13
+ node_modules
14
+ test/reports
15
+ .rubocop-http*
data/.rubocop.yml ADDED
@@ -0,0 +1,3 @@
1
+ inherit_from:
2
+ - https://raw.githubusercontent.com/workarea-commerce/workarea/master/.rubocop.yml
3
+
data/.scss-lint.yml ADDED
@@ -0,0 +1,188 @@
1
+ # Extension of the default configuration:
2
+ # https://github.com/causes/scss-lint/blob/master/config/default.yml
3
+
4
+ linters:
5
+ Comment:
6
+ enabled: false
7
+
8
+ DeclarationOrder:
9
+ enabled: true
10
+
11
+ ElsePlacement:
12
+ enabled: true
13
+ style: new_line
14
+
15
+ EmptyRule:
16
+ enabled: false
17
+
18
+ HexLength:
19
+ enabled: true
20
+ style: long
21
+
22
+ Indentation:
23
+ enabled: true
24
+ allow_non_nested_indentation: true
25
+ character: space
26
+ width: 4
27
+
28
+ LeadingZero:
29
+ enabled: true
30
+ style: include_zero
31
+
32
+ MergeableSelector:
33
+ enabled: true
34
+ force_nesting: false
35
+
36
+ PropertySortOrder:
37
+ enabled: true
38
+ ignore_unspecified: false
39
+ separate_groups: false
40
+ order:
41
+ - display
42
+ -
43
+ - position
44
+ - top
45
+ - right
46
+ - bottom
47
+ - left
48
+ - z-index
49
+ -
50
+ - margin
51
+ - margin-top
52
+ - margin-right
53
+ - margin-bottom
54
+ - margin-left
55
+ -
56
+ - margin-collapse
57
+ - margin-top-collapse
58
+ - margin-right-collapse
59
+ - margin-bottom-collapse
60
+ - margin-left-collapse
61
+ -
62
+ - padding
63
+ - padding-top
64
+ - padding-right
65
+ - padding-bottom
66
+ - padding-left
67
+ -
68
+ - width
69
+ - height
70
+ - max-width
71
+ - max-height
72
+ - min-width
73
+ - min-height
74
+ -
75
+ - float
76
+ - clear
77
+ -
78
+ - color
79
+ -
80
+ - font
81
+ - font-size
82
+ - font-style
83
+ - font-family
84
+ - font-weight
85
+ - font-variant
86
+ - font-smoothing
87
+ -
88
+ - line-height
89
+ - letter-spacing
90
+ - word-spacing
91
+ -
92
+ - text-align
93
+ - text-indent
94
+ - text-shadow
95
+ - text-overflow
96
+ - text-rendering
97
+ - text-transform
98
+ - text-decoration
99
+ - text-size-adjust
100
+ -
101
+ - word-break
102
+ - word-wrap
103
+ -
104
+ - white-space
105
+ -
106
+ - background
107
+ - background-size
108
+ - background-color
109
+ - background-image
110
+ - background-repeat
111
+ - background-position
112
+ - background-attachment
113
+ -
114
+ - border
115
+ - border-top
116
+ - border-right
117
+ - border-bottom
118
+ - border-left
119
+ -
120
+ - border-image
121
+ - border-spacing
122
+ - border-collapse
123
+ -
124
+ - border-color
125
+ - border-top-color
126
+ - border-right-color
127
+ - border-bottom-color
128
+ - border-left-color
129
+ -
130
+ - border-style
131
+ - border-top-style
132
+ - border-right-style
133
+ - border-bottom-style
134
+ - border-left-style
135
+ -
136
+ - border-width
137
+ - border-top-width
138
+ - border-right-width
139
+ - border-bottom-width
140
+ - border-left-width
141
+ -
142
+ - border-radius
143
+ - border-top-right-radius
144
+ - border-bottom-right-radius
145
+ - border-bottom-left-radius
146
+ - border-top-left-radius
147
+ - border-radius-topright
148
+ - border-radius-bottomright
149
+ - border-radius-bottomleft
150
+ - border-radius-topleft
151
+ -
152
+ - box-shadow
153
+
154
+ SelectorFormat:
155
+ enabled: true
156
+ convention: hyphenated_BEM
157
+
158
+ SingleLinePerSelector:
159
+ enabled: false
160
+
161
+ SpaceAfterPropertyColon:
162
+ enabled: true
163
+ style: at_least_one_space
164
+
165
+ SpaceBeforeBrace:
166
+ enabled: true
167
+ style: space
168
+ allow_single_line_padding: true
169
+
170
+ VariableForProperty:
171
+ enabled: true
172
+ properties:
173
+ - color
174
+ - font-family
175
+ - background-color
176
+
177
+ # These default settings may be problematic to implementors. They are not
178
+ # ommitted so that they may be adjusted as needed during an implementation.
179
+ #
180
+ # For documentation:
181
+ # https://github.com/causes/scss-lint/blob/master/lib/scss_lint/linter/README.md
182
+
183
+ DuplicateProperty:
184
+ enabled: true
185
+
186
+ PropertySpelling:
187
+ enabled: true
188
+ extra_properties: [] # Add experimental CSS to this array, if needed
data/.stylelintrc.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "stylelint-config-recommended-scss",
3
+ "rules": {
4
+ "block-no-empty": null,
5
+ "no-descending-specificity": null,
6
+ "property-no-unknown": [true, { "ignoreProperties": ["mso-hide"] }]
7
+ }
8
+ }
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --files CHANGELOG.md
data/CHANGELOG.md ADDED
@@ -0,0 +1,745 @@
1
+ Workarea Reviews 3.1.0 (2019-11-26)
2
+ --------------------------------------------------------------------------------
3
+
4
+ * Updates for v3.5 compatibility
5
+
6
+ Ben Crouse
7
+
8
+ * Initial commit on master
9
+
10
+ Curt Howard
11
+
12
+
13
+
14
+ Workarea Reviews 3.0.6 (2019-04-16)
15
+ --------------------------------------------------------------------------------
16
+
17
+ * Automatically Configure reCAPTCHA Proxy
18
+
19
+ Set the `Recaptcha.config.proxy` to `$HTTP_PROXY` in deployed
20
+ environments so developers don't have to worry about it. Update the
21
+ README to remove references to this configuration setting.
22
+
23
+ REVIEWS-144
24
+ Tom Scott
25
+
26
+ * Point Gemfile to gem server
27
+
28
+ Curt Howard
29
+
30
+
31
+
32
+ Workarea Reviews 3.0.5 (2019-03-19)
33
+ --------------------------------------------------------------------------------
34
+
35
+ * Fix Browsing Controls UI
36
+
37
+ REVIEWS-140
38
+ Curt Howard
39
+
40
+ * Update for workarea v3.4 compatibility
41
+
42
+ REVIEWS-139
43
+ Matt Duffy
44
+
45
+
46
+
47
+ Workarea Reviews 3.0.4 (2019-01-22)
48
+ --------------------------------------------------------------------------------
49
+
50
+ * Improve readme
51
+
52
+ ECOMMERCE-6523
53
+ REVIEWS-138
54
+ Chris Cressman
55
+
56
+
57
+
58
+ Workarea Reviews 3.0.3 (2018-10-30)
59
+ --------------------------------------------------------------------------------
60
+
61
+ * Change DateTime to Time in ScheduleReviewRequestsTests
62
+
63
+ Assertions began failing due to difference in nanoseconds, uncertain why but using Time instead of DateTime is a safe resolution.
64
+
65
+ REVIEWS-137
66
+ Francis Bongiovanni
67
+
68
+ * Extend list-reset trump for review group UI
69
+
70
+ REVIEWS-136
71
+ Curt Howard
72
+
73
+
74
+
75
+ Workarea Reviews 3.0.2 (2018-07-24)
76
+ --------------------------------------------------------------------------------
77
+
78
+ * Add rack attack throttle for review submissions
79
+
80
+ REVIEWS-132
81
+ Matt Duffy
82
+
83
+ * Fix Missing Title When Submitting Review
84
+
85
+ Previously, all reviews had the title and the body equal because the
86
+ title was not being passed through properly in params. We're now passing
87
+ the `title:` attribute into the Review model explicitly.
88
+
89
+ REVIEWS-130
90
+ Tom Scott
91
+
92
+
93
+
94
+ Workarea Reviews 3.0.1 (2018-06-12)
95
+ --------------------------------------------------------------------------------
96
+
97
+ * Translate hard-coded text on new review form
98
+
99
+ - Add `workarea.storefront.reviews.hints.display_name` for viewing hint on
100
+ how names are displayed.
101
+ - Add `workarea.storefront.reviews.hints.email` for viewing hint on how
102
+ email is used.
103
+
104
+ REVIEWS-130
105
+ Tom Scott
106
+
107
+
108
+
109
+ Workarea Reviews 3.0.0 (2018-05-24)
110
+ --------------------------------------------------------------------------------
111
+
112
+ * Add calculated review fields on product to data file ignore fields
113
+
114
+ REVIEWS-129
115
+ Matt Duffy
116
+
117
+ * Cancel review requests after user reviews a product from order
118
+
119
+ If a user reviews a product, either through a review request email or
120
+ directly on the site, other future review requests are canceled for
121
+ orders with that product.
122
+
123
+ REVIEWS-128
124
+ Matt Duffy
125
+
126
+ * Add administrable content to review request emails
127
+
128
+ REVIEWS-128
129
+ Matt Duffy
130
+
131
+ * set verified on a sample of review seeds
132
+
133
+ Matt Duffy
134
+
135
+ * Add importing/exporting from v3.3
136
+
137
+ ECOMMERCE-6010
138
+ Ben Crouse
139
+
140
+ * Remove import, update to work with base DataFile and bulk action changes
141
+
142
+ REVIEWS-127
143
+ Matt Duffy
144
+
145
+ * Leverage Workarea Changelog task
146
+
147
+ ECOMMERCE-5355
148
+ Curt Howard
149
+
150
+ * Update Mailers for Premailer
151
+
152
+ REVIEWS-126
153
+ Curt Howard
154
+
155
+ * Fix CHANGELOG
156
+
157
+ Curt Howard
158
+
159
+ * Update status report mailer partial to fit with new templates
160
+
161
+ Matt Duffy
162
+
163
+ * Update review request template
164
+
165
+ Matt Duffy
166
+
167
+ * Fix issues around reviews of missing products
168
+
169
+ Matt Duffy
170
+
171
+ * Update review import to align with Workarea::Import changes
172
+
173
+ REVIEWS-124
174
+ Matt Duffy
175
+
176
+ * Add verified facet and column to reviews
177
+
178
+ REVIEWS-116
179
+ Matt Duffy
180
+
181
+ * Add reviews import to admin
182
+
183
+ REVIEWS-107
184
+ Matt Duffy
185
+
186
+ * Automatically configure recaptcha for test and development
187
+
188
+ REVIEWS-117
189
+ Matt Duffy
190
+
191
+ * Disable submit button when user leaves a review
192
+
193
+ - This prevents multiple review submissions, which can happen even with recaptcha in place
194
+
195
+ REVIEWS-119
196
+ Dave Barnow
197
+
198
+ * Mark requests as sent after sending email
199
+
200
+ Matt Duffy
201
+
202
+ * Improve write a review form
203
+
204
+ * Remove fieldset and use property/label instead
205
+ * Use inline-list for rating stars wrapper
206
+ * Mark optional fields as such
207
+ * Use a grid for submit/cancel buttons for modal action consistency
208
+ * Use email_field_tag with correct attributes
209
+
210
+ REVIEWS-113
211
+ Dave Barnow
212
+
213
+ * Clean up navigation links
214
+
215
+ * Add link to marketing dashboard
216
+ * Fix link to point back to marketing dashboard
217
+ * Add jump to navigation
218
+
219
+ REVIEWS-115
220
+ Dave Barnow
221
+
222
+ * Send review request emails to users who have recently placed an order
223
+
224
+ REVIEWS-110
225
+ Matt Duffy
226
+
227
+ * Removes appended stylesheet for product_summary
228
+
229
+ REVIEWS-59
230
+ Mansi Pathak
231
+
232
+ * Removes .rating styles extended in product_summary sass
233
+
234
+ REVIEWS-59
235
+ Mansi Pathak
236
+
237
+ * Mark reviews made by users who have purchased the product as verified
238
+
239
+ REVIEWS-108
240
+ Matt Duffy
241
+
242
+ * Allow reviews without logging in. Loosen model validations
243
+
244
+ REVIEWS-112
245
+ Matt Duffy
246
+
247
+ * Cleans up sass to match code standards
248
+
249
+ * add functional vars
250
+
251
+ * extend trumps and objects
252
+
253
+ * resolve sass linter warnings
254
+
255
+ * use spacing unit for marign and padding
256
+
257
+ REVIEWS-98
258
+ Mansi Pathak
259
+
260
+ * Add summary of reviews to status report email
261
+
262
+ REVIEWS-109
263
+ Matt Duffy
264
+
265
+ * Convert index from summaries to table
266
+
267
+ REVIEWS-103
268
+ Curt Howard
269
+
270
+
271
+
272
+ Workarea Reviews 2.1.1 (2018-04-03)
273
+ --------------------------------------------------------------------------------
274
+
275
+ * Default Reviews link in Product Auxiliary Nav to sort by newest
276
+
277
+ REVIEWS-121
278
+ Dave Barnow
279
+
280
+ Workarea Reviews 2.1.0 (2017-09-15)
281
+ --------------------------------------------------------------------------------
282
+
283
+ * Adds conditional to show how many pending reviews compared to all reviews
284
+
285
+ REVIEWS-91
286
+ Ivana Veliskova
287
+
288
+ * Fix 0 Reviews State on PDP
289
+
290
+ * Shows the user there are no reviews
291
+ * lines review count display and write a review in the same row
292
+ ** allows for consistent ui across items with reviews and items without reviews
293
+
294
+ REVIEWS-90
295
+ Lucas Boyd
296
+
297
+ * Fix Review Stars UI in ie11
298
+
299
+ Add a width to write review stars to prevent them from spreading too far apart and breaking the UI
300
+
301
+ REVIEWS-96
302
+ Lucas Boyd
303
+
304
+ * Replace modernizr with feature js in CSS
305
+
306
+ Replaces modernizr selectors with the corresponding featurejs selectors
307
+
308
+ REVIEWS-94
309
+ Lucas Boyd
310
+
311
+ * Update activity with correct workarea helper for restore links
312
+
313
+ REVIEWS-95
314
+ Matt Duffy
315
+
316
+ * Add restore link to reviews for admin trash
317
+
318
+ REVIEWS-95
319
+ Matt Duffy
320
+
321
+
322
+ Workarea Reviews 2.0.5 (2017-08-22)
323
+ --------------------------------------------------------------------------------
324
+
325
+ * Adds conditional to show how many pending reviews compared to all reviews
326
+
327
+ REVIEWS-91
328
+ Ivana Veliskova
329
+
330
+ * Fix 0 Reviews State on PDP
331
+
332
+ * Shows the user there are no reviews
333
+ * lines review count display and write a review in the same row
334
+ ** allows for consistent ui across items with reviews and items without reviews
335
+
336
+ REVIEWS-90
337
+ Lucas Boyd
338
+
339
+ * Fix Review Stars UI in ie11
340
+
341
+ Add a width to write review stars to prevent them from spreading too far apart and breaking the UI
342
+
343
+ REVIEWS-96
344
+ Lucas Boyd
345
+
346
+ * Replace modernizr with feature js in CSS
347
+
348
+ Replaces modernizr selectors with the corresponding featurejs selectors
349
+
350
+ REVIEWS-94
351
+ Lucas Boyd
352
+
353
+
354
+ Workarea Reviews 2.0.4 (2017-07-07)
355
+ --------------------------------------------------------------------------------
356
+
357
+ * Wrap product ID in quotes for more relevant search results
358
+
359
+ REVIEWS-92
360
+ Dave Barnow
361
+
362
+ * Reset review attributes to defaults for product copies
363
+
364
+ REVIEWS-89
365
+ Matt Duffy
366
+
367
+
368
+ Workarea Reviews 2.0.3 (2017-06-08)
369
+ --------------------------------------------------------------------------------
370
+
371
+ * Fix test to match change to class
372
+
373
+ The interface of storefront/product changed but the test wasn't changed
374
+ to match it. Change the test to check if #sorts includes the sorting
375
+ rating.
376
+
377
+ no changelog
378
+
379
+ REVIEWS-88
380
+ Eric Pigeon
381
+
382
+ * Correct storefront product sorting by top rated, clean up admin reviews sorting
383
+
384
+ REVIEWS-83
385
+ Matt Duffy
386
+
387
+ * Expose top rated sort option to review admin. Fix issues with review sorting
388
+
389
+ REVIEWS-83
390
+ Matt Duffy
391
+
392
+ * Rename admin review view model to fit expected naming convention for search results
393
+
394
+ REVIEWS-82
395
+ Matt Duffy
396
+
397
+ * Remove jshint and replace with eslint
398
+
399
+ REVIEWS-81
400
+ Dave Barnow
401
+
402
+
403
+ Workarea Reviews 2.0.2 (2017-05-26)
404
+ --------------------------------------------------------------------------------
405
+
406
+ * Append seeds after others so that all product types are accounted for
407
+
408
+ REVIEWS-80
409
+ Dave Barnow
410
+
411
+ * Don't load helper in initialiser
412
+
413
+ This was causing a rails error when the plugin is installed in the same app as package-products.
414
+
415
+ REVIEWS-78
416
+ Beresford, Jake
417
+
418
+
419
+ Workarea Reviews 2.0.1 (2017-05-19)
420
+ --------------------------------------------------------------------------------
421
+
422
+
423
+ Workarea Reviews 2.0.0 (2017-05-17)
424
+ --------------------------------------------------------------------------------
425
+
426
+ * Simplify admin reviews UI
427
+
428
+ REVIEWS-69
429
+ Matt Duffy
430
+
431
+ * Upgrade Reviews for v3
432
+
433
+ REVIEWS-69
434
+ Eric Pigeon
435
+
436
+ * Reviews upgrade for v3 Frontend work
437
+
438
+ * Updated remaining admin translations
439
+ * Make the UI all nice and that.
440
+ * Updated admin views (summary card, edit etc.)
441
+ * Change review stars implementation to use inline svgs
442
+ * Update write review form star buttons to work with inline_svg
443
+
444
+ REVIEWS-69
445
+ Beresford, Jake
446
+
447
+ * Upgrade Reviews for v3
448
+
449
+ REVIEWS-69
450
+ Eric Pigeon
451
+
452
+
453
+ WebLinc Reviews 1.1.0 (2016-10-12)
454
+ --------------------------------------------------------------------------------
455
+
456
+ * Add activity support for v2.3
457
+
458
+ REVIEWS-66
459
+ Ben Crouse
460
+
461
+ * Improve ajax submit review feature
462
+
463
+ REVIEWS-64
464
+ Curt Howard
465
+
466
+ * Fix malformed link to reviews section on PDP
467
+
468
+ REVIEWS-62
469
+ Curt Howard
470
+
471
+ * Add teaspoon test for ajax submit reviews
472
+
473
+ REVIEWS-56
474
+ Kristen Ward
475
+
476
+ * Set up teaspoon in reviews gem
477
+
478
+ Add necessary files and settings
479
+
480
+ REVIEWS-56
481
+ Kristen Ward
482
+
483
+ * Submit reviews via ajax when submitted from dialog
484
+
485
+ Allow the 'write review' form to be opened in a dialog
486
+ and submitted via ajax to keep the user on the same page.
487
+
488
+ REVIEWS-56
489
+ Kristen Ward
490
+
491
+ * Correct the ordering of reviews on PDP to be based on created_at field.
492
+
493
+ REVIEWS-60
494
+ gharnly
495
+
496
+ * Correct the ordering of reviews on PDP to be based on created_at field.
497
+
498
+ REVIEWS-60
499
+ gharnly
500
+
501
+ * Force loading of the sort decorator before the product browse decorator
502
+
503
+ Because decorator-loading order isn't deterministic, sometimes the product browse decorator gets loaded first. This causes an error because it tries to reference a method added in the sort decorator.
504
+
505
+ REVIEWS-57
506
+ Ben Crouse
507
+
508
+ * Force loading of the sort decorator before the product browse decorator
509
+
510
+ Because decorator-loading order isn't deterministic, sometimes the product browse decorator gets loaded first. This causes an error because it tries to reference a method added in the sort decorator.
511
+
512
+ REVIEWS-57
513
+ Ben Crouse
514
+
515
+ * Add hidden-field honeypot for review bots.
516
+
517
+ Adds a `username` field on the new review form which is hidden with CSS.
518
+ If this field is present on submission we drop the request and redirect
519
+ as if it were successfully created.
520
+
521
+ REVIEWS-53
522
+ Thomas Vendetta
523
+
524
+ * Better logic and test
525
+
526
+ REVIEWS-54
527
+ Thomas Vendetta
528
+
529
+ * Merge branch 'feature/REVIEWS-54-require-users-spend-money-to-post-reviews' of ssh://stash.tools.workarea.com:7999/wl/workarea-reviews into feature/REVIEWS-54-require-users-spend-money-to-post-reviews
530
+ Thomas Vendetta
531
+
532
+ * Add visually hidden class to field
533
+
534
+ REVIEWS-53
535
+ Thomas Vendetta
536
+
537
+ * Config to require user spend money to post reviews
538
+
539
+ Adds a configuration setting that requires users to have spent money
540
+ to post a product review.
541
+
542
+ REVIEWS-54
543
+ Thomas Vendetta
544
+
545
+ * Add hidden-field honeypot for review bots.
546
+
547
+ Adds a `username` field on the new review form which is hidden with CSS.
548
+ If this field is present on submission we drop the request and redirect
549
+ as if it were successfully created.
550
+
551
+ REVIEWS-53
552
+ Thomas Vendetta
553
+
554
+ * Fix read/write review scroll_to_button bug
555
+
556
+ The `anchor` param being passed to some route helpers was being
557
+ improperly merged into the options hash. This commit separates this
558
+ param from the other options passed to the helper.
559
+
560
+ REVIEWS-52
561
+ Curt Howard
562
+
563
+
564
+ WebLinc Reviews 1.0.5 (2016-08-30)
565
+ --------------------------------------------------------------------------------
566
+
567
+ * Fix malformed link to reviews section on PDP
568
+
569
+ REVIEWS-62
570
+ Curt Howard
571
+
572
+
573
+ WebLinc Reviews 1.0.4 (2016-05-09)
574
+ --------------------------------------------------------------------------------
575
+
576
+ * Correct the ordering of reviews on PDP to be based on created_at field.
577
+
578
+ REVIEWS-60
579
+ gharnly
580
+
581
+
582
+ WebLinc Reviews 1.0.3 (2016-04-08)
583
+ --------------------------------------------------------------------------------
584
+
585
+ * Force loading of the sort decorator before the product browse decorator
586
+
587
+ Because decorator-loading order isn't deterministic, sometimes the product browse decorator gets loaded first. This causes an error because it tries to reference a method added in the sort decorator.
588
+
589
+ REVIEWS-57
590
+ Ben Crouse
591
+
592
+
593
+ WebLinc Reviews 1.0.2 (2016-04-05)
594
+ --------------------------------------------------------------------------------
595
+
596
+
597
+ WebLinc Reviews 1.0.1 (January 26, 2016)
598
+ --------------------------------------------------------------------------------
599
+
600
+ * Fix read/write review scroll_to_button bug
601
+
602
+ The `anchor` param being passed to some route helpers was being
603
+ improperly merged into the options hash. This commit separates this
604
+ param from the other options passed to the helper.
605
+
606
+ REVIEWS-52
607
+
608
+
609
+ Unreleased
610
+ --------------------------------------------------------------------------------
611
+
612
+ * Config to require user spend money to post reviews
613
+
614
+ Adds a configuration setting that requires users to have spent money
615
+ to post a product review.
616
+
617
+ REVIEWS-54
618
+
619
+ * Add hidden-field honeypot for review bots.
620
+
621
+ Adds a `username` field on the new review form which is hidden with CSS.
622
+ If this field is present on submission we drop the request and redirect
623
+ as if it were successfully created.
624
+
625
+ REVIEWS-53
626
+
627
+
628
+ WebLinc Reviews 1.0.0 (January 13, 2016)
629
+ --------------------------------------------------------------------------------
630
+
631
+ * Update for compatibility with WebLinc 2.0
632
+
633
+ * Replace absolute URLs with relative paths
634
+
635
+
636
+ WebLinc Reviews 0.10.0 (October 7, 2015)
637
+ --------------------------------------------------------------------------------
638
+
639
+ * Add metadata and update context-menu
640
+
641
+ REVIEWS-50
642
+
643
+ * Update plugin to be compatible with v0.12
644
+
645
+ Update new & edit views, property work
646
+ Add blank row to permissions partial
647
+ Update indexes, add context-menu to summary/edit
648
+
649
+ REVIEWS-50
650
+
651
+ * Update menu for compatibility with ECOMMERCE-1344
652
+
653
+ REVIEWS-45
654
+
655
+ * Fix presentation of dashboard pending reviews count
656
+
657
+ Fix markup within dashboard to do list partial to match the to do list
658
+ markup from workarea.
659
+
660
+ REVIEWS-51
661
+
662
+ * Update sort by property js template with correct class name
663
+
664
+ REVIEWS-46
665
+
666
+
667
+ WebLinc Reviews 0.9.0 (August 21, 2015)
668
+ --------------------------------------------------------------------------------
669
+
670
+ * Allow reviews without a user so that reviews from legacy applications can
671
+ be imported.
672
+
673
+ REVIEWS-44
674
+
675
+ 826ca2a5c96062bc4e0be5adb2887baec76effbd
676
+ 3b578531e3a3d23d292cffb592a3a53b2c63e42b
677
+
678
+ * Rename SCSS blocks `panel` and `panel--buttons` to `index-filters` and
679
+ `form-actions`, respectively, for compatibility with WebLinc 0.11.
680
+
681
+ REVIEWS-43
682
+
683
+ c2a5cbfd09014160afe7ed5039f60ce229e4f096
684
+
685
+ * Use vector images for review stars. Re-write display code accordingly.
686
+
687
+ REVIEWS-34
688
+
689
+ 491e6bad575c921e5e6f57f4962b45f4672c18db (merge)
690
+
691
+
692
+ WebLinc Reviews 0.8.0 (July 12, 2015)
693
+ --------------------------------------------------------------------------------
694
+
695
+ * Add model summaries to Admin indexes.
696
+
697
+ REVIEWS-39
698
+
699
+ d462476e7bd817f505c703c7cf7daa66874e7da3
700
+ 410b6dd1f2590178860421784a9adec0a4520c4d
701
+ 1f52be2a94898d03639b82a0bdeafde146b564d6
702
+ bd21b18c9c65fa6a9010098f63ed877f1ce65389
703
+
704
+ * Update for compatibility with workarea 0.10 and constrain to workarea 0.10.
705
+
706
+ eaa21d516260b6bdd20cffdfdf6f260a314e80c6
707
+ e641252d86ac34cecb696a992cec2976514a2946
708
+ b0a1b40cb10f10dd7b287331d2ef5c0584544dcd
709
+ 5606d9fe4eac382d120266b79bfdf0fcbfc18953
710
+ 7d251907578264886982393b821056f614d8c87c
711
+ 170ba9bdaf8aa60cd47d4a0030e20048e3c347f6
712
+ 5babadcb3270b4d6c2f06e9fd68efe3ad6fdf35b
713
+ dd669d4b31f0f20ff51b100a57274e2d39ef897e
714
+
715
+ * Fix "back" links in Admin.
716
+
717
+ REVIEWS-41
718
+
719
+ 696b1698c97cc08915588db304a67a931ee57a0b
720
+
721
+
722
+ WebLinc Reviews 0.7.0 (June 1, 2015)
723
+ --------------------------------------------------------------------------------
724
+
725
+ * Rename fixtures to factories and clean up factories.
726
+
727
+ * Update for compatibility and consistency with workarea 0.9.0.
728
+
729
+ * Remove pagination from reviews in Store Front. Display all reviews.
730
+
731
+ REVIEWS-20
732
+
733
+
734
+ WebLinc Reviews 0.6.0 (April 10, 2015)
735
+ --------------------------------------------------------------------------------
736
+
737
+ * Update JavaScript modules for compatibility with WebLinc 0.8.0.
738
+
739
+ * Update testing environment for compatibility with WebLinc 0.8.0.
740
+
741
+ * Use new decorator style for consistency with WebLinc 0.8.0.
742
+
743
+ * Remove gems server secrets for consistency with WebLinc 0.8.0.
744
+
745
+ * Update assets for compatibility with WebLinc 0.8.0.
data/CODE_OF_CONDUCT.md ADDED
@@ -0,0 +1,3 @@
1
+ View this plugin's code of conduct here:
2
+
3
+ <https://github.com/workarea-commerce/workarea/blob/master/CODE_OF_CONDUCT.md>
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,3 @@
1
+ View this plugin's contribution guidelines here:
2
+
3
+ <https://github.com/workarea-commerce/workarea/blob/master/CONTRIBUTING.md>
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
+
4
+ gemspec
5
+
6
+ gem 'byebug'
7
+
8
+ gem 'workarea', github: 'workarea-commerce/workarea'
data/LICENSE ADDED
@@ -0,0 +1,52 @@
1
+ WebLinc
2
+ Business Source License
3
+
4
+ Licensor: WebLinc Corporation, 22 S. 3rd Street, 2nd Floor, Philadelphia PA 19106
5
+
6
+ Licensed Work: Workarea Commerce Platform
7
+ The Licensed Work is (c) 2019 WebLinc Corporation
8
+
9
+ Additional Use Grant:
10
+ You may make production use of the Licensed Work without an additional license agreement with WebLinc so long as you do not use the Licensed Work for a Commerce Service.
11
+
12
+ A "Commerce Service" is a commercial offering that allows third parties (other than your employees and contractors) to access the functionality of the Licensed Work by creating or managing commerce functionality, the products, taxonomy, assets and/or content of which are controlled by such third parties.
13
+
14
+ For information about obtaining an additional license agreement with WebLinc, contact licensing@workarea.com.
15
+
16
+ Change Date: 2019-08-20
17
+
18
+ Change License: Version 2.0 or later of the GNU General Public License as published by the Free Software Foundation
19
+
20
+ Terms
21
+
22
+ The Licensor hereby grants you the right to copy, modify, create derivative works, redistribute, and make non-production use of the Licensed Work. The Licensor may make an Additional Use Grant, above, permitting limited production use.
23
+
24
+ Effective on the Change Date, or the fourth anniversary of the first publicly available distribution of a specific version of the Licensed Work under this License, whichever comes first, the Licensor hereby grants you rights under the terms of the Change License, and the rights granted in the paragraph above terminate.
25
+
26
+ If your use of the Licensed Work does not comply with the requirements currently in effect as described in this License, you must purchase a commercial license from the Licensor, its affiliated entities, or authorized resellers, or you must refrain from using the Licensed Work.
27
+
28
+ All copies of the original and modified Licensed Work, and derivative works of the Licensed Work, are subject to this License. This License applies separately for each version of the Licensed Work and the Change Date may vary for each version of the Licensed Work released by Licensor.
29
+
30
+ You must conspicuously display this License on each original or modified copy of the Licensed Work. If you receive the Licensed Work in original or modified form from a third party, the terms and conditions set forth in this License apply to your use of that work.
31
+
32
+ Any use of the Licensed Work in violation of this License will automatically terminate your rights under this License for the current and all other versions of the Licensed Work.
33
+
34
+ This License does not grant you any right in any trademark or logo of Licensor or its affiliates (provided that you may use a trademark or logo of Licensor as expressly required by this License). TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE. MariaDB hereby grants you permission to use this License’s text to license your works and to refer to it using the trademark "Business Source License" as long as you comply with the Covenants of Licensor below.
35
+
36
+ Covenants of Licensor
37
+ In consideration of the right to use this License’s text and the "Business Source License" name and trademark, Licensor covenants to MariaDB, and to all other recipients of the licensed work to be provided by Licensor:
38
+
39
+ To specify as the Change License the GPL Version 2.0 or any later version, or a license that is compatible with GPL Version 2.0 or a later version, where "compatible" means that software provided under the Change License can be included in a program with software provided under GPL Version 2.0 or a later version. Licensor may specify additional Change Licenses without limitation.
40
+
41
+ To either: (a) specify an additional grant of rights to use that does not impose any additional restriction on the right granted in this License, as the Additional Use Grant; or (b) insert the text "None."
42
+
43
+ To specify a Change Date.
44
+
45
+ Not to modify this License in any other way.
46
+
47
+ Notice
48
+ The Business Source License (this document, or the "License") is not an Open Source license. However, the Licensed Work will eventually be made available under an Open Source License, as stated in this License.
49
+
50
+ For more information on the use of the Business Source License generally, please visit the Adopting and Developing Business Source License FAQ.
51
+
52
+ License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved. "Business Source License" is a trademark of MariaDB Corporation Ab.
data/README.md ADDED
@@ -0,0 +1,107 @@
1
+ Workarea Reviews
2
+ ================================================================================
3
+
4
+ A Workarea Commerce plugin that enables product reviews.
5
+
6
+
7
+ Overview
8
+ --------------------------------------------------------------------------------
9
+
10
+ * Consumers can write product reviews in the Storefront
11
+ * Admins can edit those reviews in the Admin, which includes approving them
12
+ * Approved reviews are reflected in product summaries and displayed on product detail pages
13
+ * Consumers can sort products by rating in the Storefront
14
+ * Admins can list, show, edit, and delete reviews in the Admin
15
+ * Admins can view and navigate to a product's reviews when administrating a product in the Admin
16
+ * The Admin daily status report email includes a summary of reviews
17
+ * The system emails consumers who place orders to request they write reviews
18
+ * Developers can seed reviews for all products
19
+ * Developers can preview review-related emails
20
+ * Admins can view a reviews by product report
21
+ * The system generates top rated and most reviewed product insights
22
+ * The system generates most active reviewers insight
23
+
24
+ Getting Started
25
+ --------------------------------------------------------------------------------
26
+
27
+ Add the gem to your application's Gemfile:
28
+
29
+ ```ruby
30
+ # ...
31
+ gem 'workarea-reviews'
32
+ # ...
33
+ ```
34
+
35
+ Update your application's bundle.
36
+
37
+ ```bash
38
+ cd path/to/application
39
+ bundle
40
+ ```
41
+
42
+ Features
43
+ --------------------------------------------------------------------------------
44
+
45
+
46
+ ### Writing Reviews
47
+
48
+ * Reviews are written in the Storefront; users may be logged in or anonymous (requires reCAPTCHA)
49
+ * Developers can set `Workarea.config.require_purchase_to_write_review` to `true` to require the reviewer to have a `total_spent` greater than zero for the review to be valid
50
+ * The "write review" form is presented as a dialog, which closes after the review is submitted
51
+ * The reviewer uses a rating star UI (with underlying values of 1 through 5) to select a rating
52
+ * The reviewer may also provide a title and body, along with their first name, last name, and email
53
+ * The reviewer receives an email to confirm the review was submitted and is awaiting admin approval
54
+
55
+
56
+ ### Displaying Reviews
57
+
58
+ * In the Storefront, reviews are presented without pagination
59
+ * Reviews can be sorted, which is handled client-side (values are oldest, newest, and highest)
60
+ * Each review may include a title, a body, and the following metadata: author, date, verified
61
+ * A review is considered verified if the reviewer was logged in and has the ID of the reviewed product in their order history
62
+
63
+
64
+ ### Administrating Reviews
65
+
66
+ * Admins with marketing permission may access the reviews admin
67
+ * The reviews admin is accessible from the Marketing section of the primary navigation and from the Marketing dashboard
68
+ * Admins can also jump to the reviews admin by searching for "Product Reviews"
69
+ * Products and reviews are cross referenced within the auxillary navigation of each
70
+ * Admins can list, show, edit, and delete reviews, which includes approving them (Admins can edit a review before approving)
71
+
72
+
73
+ ### Requesting Reviews
74
+
75
+ * For each order, the system will send up to _n_ review requests (Workarea.config.review_requests_per_order)
76
+ * Each request is for a different order item (selected by most expensive)
77
+ * Each request expires after a configurable duration (Workarea.config.review_request_ttl)
78
+ * The first request is sent after a configurable duration has passed (Workarea.config.review_request_initial_delivery_delay)
79
+ * Each consecutive request is sent at a configurable duration (Workarea.config.review_request_secondary_delivery_delay)
80
+
81
+ reCAPTCHA Configuration
82
+ --------------------------------------------------------------------------------
83
+
84
+ Workarea Reviews utilizes [reCAPTCHA](https://github.com/ambethia/recaptcha) in the review form to prevent abuse. You'll need to configure each environment's secrets file to provide recaptcha keys. For testing and development, the plugin will use the [google provided](https://developers.google.com/recaptcha/docs/faq) testing keys if none are specified in your secrets.
85
+
86
+ ```yaml
87
+ production:
88
+ recaptcha:
89
+ site_key: YOUR_SITE_KEY
90
+ secret_key: YOUR_SECRET_KEY
91
+ ```
92
+
93
+ reCAPTCHA sends its HTTP requests through the proxy automatically if the
94
+ `$HTTP_PROXY` environment variable is set. This is set for you in hosted
95
+ environments.
96
+
97
+ [Obtain your keys from the reCAPTCHA site](https://www.google.com/recaptcha/admin)
98
+
99
+ Workarea Commerce Documentation
100
+ --------------------------------------------------------------------------------
101
+
102
+ See [https://developer.workarea.com](https://developer.workarea.com) for Workarea Commerce documentation.
103
+
104
+ License
105
+ --------------------------------------------------------------------------------
106
+
107
+ Workarea Reviews is released under the [Business Software License](LICENSE)
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
9
+ load 'rails/tasks/engine.rake'
10
+ load 'rails/tasks/statistics.rake'
11
+ load 'workarea/changelog.rake'
12
+
13
+ require 'rake/testtask'
14
+
15
+ Rake::TestTask.new(:test) do |t|
16
+ t.libs << 'lib'
17
+ t.libs << 'test'
18
+ t.pattern = 'test/**/*_test.rb'
19
+ t.verbose = false
20
+ end
21
+
22
+ task default: :test
23
+
24
+ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
25
+ require 'workarea/reviews/version'
26
+
27
+ desc "Release version #{Workarea::Reviews::VERSION} of the gem"
28
+ task :release do
29
+ host = "https://#{ENV['BUNDLE_GEMS__WEBLINC__COM']}@gems.weblinc.com"
30
+
31
+ Rake::Task['workarea:changelog'].execute
32
+ system 'git add CHANGELOG.md'
33
+ system 'git commit -m "Update CHANGELOG"'
34
+ system 'git push origin HEAD'
35
+
36
+ system "git tag -a v#{Workarea::Reviews::VERSION} -m 'Tagging #{Workarea::Reviews::VERSION}'"
37
+ system 'git push --tags'
38
+
39
+ system 'gem build workarea-reviews.gemspec'
40
+ system "gem push workarea-reviews-#{Workarea::Reviews::VERSION}.gem --host #{host}"
41
+ system "rm workarea-reviews-#{Workarea::Reviews::VERSION}.gem"
42
+ end
43
+
44
+ desc 'Run the JavaScript tests'
45
+ ENV['TEASPOON_RAILS_ENV'] = File.expand_path('../test/dummy/config/environment', __FILE__)
46
+ task teaspoon: 'app:teaspoon'
47
+
48
+ desc 'Start a server at http://localhost:3000/teaspoon for JavaScript tests'
49
+ task :teaspoon_server do
50
+ Dir.chdir('test/dummy')
51
+ teaspoon_env = File.expand_path('../test/teaspoon_env.rb', __FILE__)
52
+ system "RAILS_ENV=test TEASPOON_ENV=#{teaspoon_env} rails s"
53
+ end
data/app/assets/images/workarea/admin/icons/star.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>star</title><path d="M12 19.41l-7.33 3.84 1.43-8.13L.17 9.36l8.18-1.2L12 .75l3.65 7.41 8.19 1.2-5.94 5.76 1.42 8.13L12 19.41z"/></svg>
data/app/assets/images/workarea/storefront/icons/empty_star.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>empty_star</title><path d="M12 3l2.75 5.6.23.47.52.08 6.18.91-4.48 4.34-.38.37.09.52L18 21.42l-5.52-2.9-.48-.24-.46.24L6 21.42l1.08-6.13.09-.52-.37-.37-4.48-4.34 6.18-.91.5-.08.23-.47L12 3m0-2.25L8.35 8.16.17 9.36l5.93 5.76-1.43 8.13L12 19.41l7.32 3.84-1.42-8.13 5.94-5.76-8.19-1.2L12 .75z"/></svg>
data/app/assets/images/workarea/storefront/icons/half_star.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>half_star</title><path d="M12 19.41V.75L8.35 8.16.17 9.36l5.93 5.76-1.43 8.13L12 19.41z"/><path d="M12 3l2.75 5.6.23.47.52.08 6.18.91-4.48 4.34-.38.37.09.52L18 21.42l-5.52-2.9-.48-.24-.46.24L6 21.42l1.08-6.13.09-.52-.37-.37-4.48-4.34 6.18-.91.5-.08.23-.47L12 3m0-2.25L8.35 8.16.17 9.36l5.93 5.76-1.43 8.13L12 19.41l7.32 3.84-1.42-8.13 5.94-5.76-8.19-1.2L12 .75z"/></svg>
data/app/assets/images/workarea/storefront/icons/star.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>star</title><path d="M12 19.41l-7.33 3.84 1.43-8.13L.17 9.36l8.18-1.2L12 .75l3.65 7.41 8.19 1.2-5.94 5.76 1.42 8.13L12 19.41z"/></svg>
data/app/assets/javascripts/workarea/storefront/reviews/modules/product_review_ajax_submit.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @namespace WORKAREA.productReviewAjaxSubmit
3
+ */
4
+ WORKAREA.registerModule('productReviewAjaxSubmit', (function () {
5
+ 'use strict';
6
+
7
+ var submitForm = function ($form, event) {
8
+ if ($form.valid() === false) { return; }
9
+
10
+ event.preventDefault();
11
+
12
+ $.ajax({
13
+ url: $form.attr('action'),
14
+ type: $form.attr('method'),
15
+ data: $form.serialize()
16
+ }).done(_.partial(WORKAREA.dialog.closeClosest, $form));
17
+ },
18
+
19
+ formNotInDialog = function ($form) {
20
+ return _.isEmpty($form.closest('.ui-dialog'));
21
+ },
22
+
23
+ /**
24
+ * @method
25
+ * @name init
26
+ * @memberof WORKAREA.productReviewAjaxSubmit
27
+ */
28
+ init = function ($scope) {
29
+ var $form = $('[data-product-review-ajax-submit]', $scope);
30
+
31
+ if (formNotInDialog($form)) { return; }
32
+
33
+ $form.on('submit', _.partial(submitForm, $form));
34
+ };
35
+
36
+ return {
37
+ init: init
38
+ };
39
+ }()));
data/app/assets/javascripts/workarea/storefront/reviews/modules/product_reviews_sort_menus.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @namespace WORKAREA.productReviewsSortMenus
3
+ */
4
+ WORKAREA.registerModule('productReviewsSortMenus', (function () {
5
+ 'use strict';
6
+
7
+ var insertSortSelectBox = function (index, section) {
8
+ $('.reviews__header', section).after(
9
+ JST['workarea/storefront/reviews/templates/sort_by_property']()
10
+ );
11
+ },
12
+
13
+ sortNewToOld = function ($reviews) {
14
+ return _.sortBy($reviews, function (review) {
15
+ return -($(review).data('productReviewSectionEntry').createdAt);
16
+ });
17
+ },
18
+
19
+ sortOldToNew = function ($reviews) {
20
+ return _.sortBy($reviews, function (review) {
21
+ return $(review).data('productReviewSectionEntry').createdAt;
22
+ });
23
+ },
24
+
25
+
26
+ sortHighToLow = function ($reviews) {
27
+ return _.sortBy($reviews, function (review) {
28
+ return -($(review).data('productReviewSectionEntry').rating);
29
+ });
30
+ },
31
+
32
+ sortLowToHigh = function ($reviews) {
33
+ return _.sortBy($reviews, function (review) {
34
+ return $(review).data('productReviewSectionEntry').rating;
35
+ });
36
+ },
37
+
38
+ handleSortSelectChange = function (event) {
39
+ var $reviews = $(
40
+ '[data-product-review-section-entry]', event.delegateTarget
41
+ ),
42
+
43
+ action = $(event.currentTarget).val(),
44
+
45
+ actions = {
46
+ oldest: function () {
47
+ return sortOldToNew($reviews);
48
+ },
49
+ newest: function () {
50
+ return sortNewToOld($reviews);
51
+ },
52
+ lowest: function () {
53
+ return sortLowToHigh($reviews);
54
+ },
55
+ highest: function () {
56
+ return sortHighToLow($reviews);
57
+ }
58
+ };
59
+
60
+ $('.reviews__review-group', event.delegateTarget)
61
+ .html(actions[action]());
62
+ },
63
+
64
+ /**
65
+ * @method
66
+ * @name init
67
+ * @memberof WORKAREA.productReviewsSortMenus
68
+ */
69
+ init = function ($scope) {
70
+ $('.reviews', $scope)
71
+ .each(insertSortSelectBox)
72
+ .on(
73
+ 'change',
74
+ '[data-product-reviews-sort-menu]',
75
+ handleSortSelectChange
76
+ );
77
+ };
78
+
79
+ return {
80
+ init: init
81
+ };
82
+ }()));
data/app/assets/javascripts/workarea/storefront/reviews/modules/rating_buttons.js ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @namespace WORKAREA.ratingButtons
3
+ */
4
+ WORKAREA.registerModule('ratingButtons', (function () {
5
+ 'use strict';
6
+
7
+ var activateLabels = function (radioButton, originalIndex, $scope) {
8
+ var $allButtons = getAllRatingButtons($(document)),
9
+
10
+ updateState = function (index, button) {
11
+ var $label = getAssociatedLabel(button, $scope);
12
+
13
+ $label.removeClass('write-review__star--hovered write-review__star--active');
14
+
15
+ if ($label.data('index') <= originalIndex) {
16
+ $label.addClass('write-review__star--active');
17
+ }
18
+ };
19
+
20
+ if ($(radioButton).is(':checked')) {
21
+ $allButtons.each(updateState);
22
+ }
23
+ },
24
+
25
+ getAllRatingButtons = function ($scope) {
26
+ return $('[data-rating-button]', $scope);
27
+ },
28
+
29
+ getAssociatedLabel = function (button, $scope) {
30
+ return $('label[for=' + $(button).attr('id') + ']', $scope);
31
+ },
32
+
33
+ removeHoverClass = function ($label) {
34
+ $label.removeClass('write-review__star--hovered');
35
+ },
36
+
37
+ addHoverClass = function ($label) {
38
+ $label.addClass('write-review__star--hovered');
39
+ },
40
+
41
+ removeHoverStateFromLabel = function ($scope) {
42
+ var $buttons = getAllRatingButtons($scope),
43
+
44
+ removeState = function (index, button) {
45
+ var $label = getAssociatedLabel(button, $scope);
46
+
47
+ removeHoverClass($label);
48
+ };
49
+
50
+ $buttons.each(removeState);
51
+ },
52
+
53
+ addHoverStateToLabels = function ($scope, originalIndex) {
54
+ var $buttons = getAllRatingButtons($scope),
55
+
56
+ addState = function (index, button) {
57
+ var $label = getAssociatedLabel(button, $scope);
58
+
59
+ if ($label.data('index') <= originalIndex) {
60
+ addHoverClass($label);
61
+ }
62
+ };
63
+
64
+ $buttons.each(addState);
65
+ },
66
+
67
+ setupLabels = function ($scope, index, button) {
68
+ var $label = getAssociatedLabel(button, $scope),
69
+ labelTitle = $('.write-review__star-text', $label).text();
70
+
71
+ /**
72
+ * 1. per http://www.workarea.com/labs/touching-on-html-labels/
73
+ */
74
+
75
+ $label
76
+ .attr('title', labelTitle)
77
+ .data('index', index)
78
+ .on('click', $.noop) /* [1] */
79
+ .on('mouseenter', _.partial(addHoverStateToLabels, $scope, index))
80
+ .on('mouseleave', _.partial(removeHoverStateFromLabel, $scope));
81
+
82
+ activateLabels(button, index, $scope);
83
+
84
+ $(button)
85
+ .on('click', _.partial(activateLabels, button, index, $scope))
86
+ .on('focus', _.partial(addHoverClass, $label))
87
+ .on('blur', _.partial(removeHoverClass, $label));
88
+ },
89
+
90
+ /**
91
+ * @method
92
+ * @name init
93
+ * @memberof WORKAREA.ratingButtons
94
+ */
95
+ init = function ($scope) {
96
+ $('[data-rating-button]', $scope)
97
+ .each(_.partial(setupLabels, $scope));
98
+ };
99
+
100
+ return {
101
+ init: init
102
+ };
103
+ }()));
data/app/assets/javascripts/workarea/storefront/reviews/templates/sort_by_property.jst.ejs ADDED
@@ -0,0 +1,11 @@
1
+ <div class="property property--responsive">
2
+ <label class="property__name"><span class="property__text">Sort By</span></label>
3
+ <div class="value">
4
+ <select data-product-reviews-sort-menu="">
5
+ <option value="newest" selected>Most Recent</option>
6
+ <option value="oldest">Oldest First</option>
7
+ <option value="highest">Rating: High to Low</option>
8
+ <option value="lowest">Rating: Low to High</option>
9
+ </select>
10
+ </div>
11
+ </div>
data/app/assets/stylesheets/workarea/storefront/reviews/components/_rating.scss ADDED
@@ -0,0 +1,28 @@
1
+ /*------------------------------------*\
2
+ #RATING
3
+ \*------------------------------------*/
4
+
5
+ $rating-fill-color: $yellow !default;
6
+ $rating-empty-color: $gray !default;
7
+ $rating-star-size: 16px !default;
8
+
9
+
10
+
11
+ .rating {
12
+ position: relative;
13
+ white-space: nowrap;
14
+ vertical-align: middle;
15
+ user-select: none;
16
+
17
+ .product-summary & {
18
+ margin: $spacing-unit auto 0;
19
+ }
20
+ }
21
+
22
+ .rating__star {
23
+ display: inline-block;
24
+ width: $rating-star-size;
25
+ height: $rating-star-size;
26
+ fill: $rating-fill-color;
27
+ overflow: hidden;
28
+ }
data/app/assets/stylesheets/workarea/storefront/reviews/components/_reviews.scss ADDED
@@ -0,0 +1,59 @@
1
+ /*------------------------------------*\
2
+ #REVIEWS
3
+ \*------------------------------------*/
4
+
5
+ .reviews {
6
+ margin: ($spacing-unit * 3) 0 0;
7
+ }
8
+
9
+ .reviews__header {
10
+ display: table;
11
+ margin: 0 0 ($spacing-unit * 2);
12
+ width: 100%;
13
+ }
14
+
15
+ .reviews__count {
16
+ display: table-cell;
17
+ }
18
+
19
+ .reviews__write-action {
20
+ display: table-cell;
21
+ text-align: right;
22
+ }
23
+
24
+ .reviews__review-group {
25
+ @extend %list-reset;
26
+ }
27
+
28
+ .reviews__review {
29
+ padding: ($spacing-unit * 2) 0;
30
+ }
31
+
32
+ .reviews__review-title {}
33
+
34
+ .reviews__review-body {}
35
+
36
+ .reviews__review-meta {
37
+ @extend %clearfix;
38
+ }
39
+
40
+ .reviews__review-author {
41
+ margin: 0;
42
+ float: left;
43
+ line-height: 1;
44
+ }
45
+
46
+ .reviews__review-verified {
47
+ margin: 0 0 0 $spacing-unit;
48
+ padding-left: $spacing-unit;
49
+ float: left;
50
+ line-height: 1;
51
+ font-weight: bold;
52
+ }
53
+
54
+ .reviews__review-date {
55
+ margin: 0 0 0 $spacing-unit;
56
+ padding-left: $spacing-unit;
57
+ float: left;
58
+ line-height: 1;
59
+ }
data/app/assets/stylesheets/workarea/storefront/reviews/components/_reviews_aggregate.scss ADDED
@@ -0,0 +1,38 @@
1
+ /*------------------------------------*\
2
+ #REVIEWS-AGGREGATE
3
+ \*------------------------------------*/
4
+
5
+ $reviews-aggregate-border: 1px solid $link-color !default;
6
+
7
+
8
+ .reviews-aggregate {
9
+ @extend %clearfix;
10
+ margin: 0 0 12px;
11
+
12
+ /**
13
+ * Extend `.rating {}` in _rating.scss.
14
+ */
15
+
16
+ .rating {
17
+ @extend %inline-list;
18
+ margin: 0 ($spacing-unit / 2) 0 0;
19
+ }
20
+ }
21
+
22
+ .reviews-aggregate__rating-link:hover {
23
+ text-decoration: none;
24
+ }
25
+
26
+ .reviews-aggregate__count {
27
+ @extend %inline-list;
28
+ }
29
+
30
+ .reviews-aggregate__read {}
31
+
32
+ .reviews-aggregate__write-action {
33
+ &::before {
34
+ padding-right: ($spacing-unit / 2);
35
+ border-left: $reviews-aggregate-border;
36
+ content: '';
37
+ }
38
+ }
data/app/assets/stylesheets/workarea/storefront/reviews/components/_write_review.scss ADDED
@@ -0,0 +1,63 @@
1
+ /*------------------------------------*\
2
+ #WRITE-REVIEW
3
+ \*------------------------------------*/
4
+
5
+ $write-review-fill-color: $yellow !default;
6
+ $write-review-empty-color: $transparent !default;
7
+ $write-review-star-size: 16px !default;
8
+
9
+
10
+ .write-review {}
11
+
12
+ .write-review__ratings {
13
+ @extend %inline-list;
14
+
15
+
16
+ .js & {
17
+ @extend %clearfix;
18
+ }
19
+ }
20
+
21
+ .write-review__rating {
22
+ font-size: $write-review-star-size;
23
+
24
+ .js & {
25
+ padding-right: ($spacing-unit / 2);
26
+ float: left;
27
+ }
28
+ }
29
+
30
+ .write-review__rating-input {
31
+ margin: 0 ($spacing-unit / 2) 0 0;
32
+
33
+ .js & {
34
+ @extend %visually-hidden;
35
+ }
36
+ }
37
+
38
+ .write-review__star {}
39
+
40
+ .write-review__star-icon {
41
+ width: $write-review-star-size;
42
+ height: $write-review-star-size;
43
+ fill: $write-review-empty-color;
44
+ stroke: $write-review-fill-color;
45
+
46
+ .write-review__star--hovered &,
47
+ .write-review__star--active & {
48
+ fill: $write-review-fill-color;
49
+ }
50
+ }
51
+
52
+
53
+ .write-review__star--hovered {
54
+ opacity: 0.4;
55
+ }
56
+
57
+ .write-review__star--active {
58
+ opacity: 1;
59
+ }
60
+
61
+ .write-review__star-text {
62
+ @extend %hidden-if-js-enabled;
63
+ }
data/app/controllers/workarea/admin/catalog_products_controller.decorator ADDED
@@ -0,0 +1,9 @@
1
+ module Workarea
2
+ decorate Admin::CatalogProductsController, with: :reviews do
3
+ decorated { helper Admin::ReviewsHelper }
4
+
5
+ def reviews
6
+ @reviews = Review.find_for_product(@product.id, true)
7
+ end
8
+ end
9
+ end
data/app/controllers/workarea/admin/reports_controller.decorator ADDED
@@ -0,0 +1,10 @@
1
+ module Workarea
2
+ decorate Admin::ReportsController, with: :reviews do
3
+ def reviews_by_product
4
+ @report = Admin::Reports::ReviewsByProductViewModel.wrap(
5
+ Workarea::Reports::ReviewsByProduct.new(params),
6
+ view_model_options
7
+ )
8
+ end
9
+ end
10
+ end
data/app/controllers/workarea/admin/reviews_controller.rb ADDED
@@ -0,0 +1,51 @@
1
+ module Workarea
2
+ class Admin::ReviewsController < Admin::ApplicationController
3
+ required_permissions :marketing
4
+ before_action :find_review, except: :index
5
+
6
+ def index
7
+ search = Search::AdminReviews.new(params.merge(autocomplete: request.xhr?))
8
+ @search = Admin::ReviewsSearchViewModel.new(search, view_model_options)
9
+ end
10
+
11
+ def show
12
+ redirect_to edit_review_path(@review)
13
+ end
14
+
15
+ def edit; end
16
+
17
+ def update
18
+ if @review.update_attributes(review_params)
19
+ flash[:success] = t('workarea.admin.reviews.flash_messages.updated')
20
+ redirect_to reviews_path
21
+ else
22
+ render :edit
23
+ end
24
+ end
25
+
26
+ def destroy
27
+ @review.destroy
28
+ flash[:success] = t('workarea.admin.reviews.flash_messages.destroyed')
29
+ if params[:product].present?
30
+ redirect_to reviews_catalog_product_path(params[:product])
31
+ else
32
+ redirect_to reviews_path
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def find_review
39
+ if params[:id].present?
40
+ review = Review.find(params[:id])
41
+ @review = Admin::ReviewViewModel.wrap(review)
42
+ @user = User.find(review.user_id) rescue nil
43
+ end
44
+ end
45
+
46
+ def review_params
47
+ return {} if params[:review].blank?
48
+ params[:review].permit(:body, :approved, :rating, :title)
49
+ end
50
+ end
51
+ end
data/app/controllers/workarea/storefront/review_requests_controller.rb ADDED
@@ -0,0 +1,50 @@
1
+ module Workarea
2
+ module Storefront
3
+ class ReviewRequestsController < Storefront::ApplicationController
4
+ before_action :validate_request
5
+ before_action :find_product, :set_review
6
+
7
+ def show; end
8
+
9
+ def complete
10
+ if verify_recaptcha(model: @review, env: Rails.env) && @review.save
11
+ @request.complete!
12
+ Review::Request.cancel_for_orders!(@request.order_id)
13
+
14
+ flash[:success] = t('workarea.storefront.reviews.flash_messages.created')
15
+ redirect_to product_path(@product)
16
+ else
17
+ flash[:error] = t('workarea.storefront.reviews.flash_messages.failure')
18
+ render :show
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def validate_request
25
+ @request = Review::Request.find_by_token(params[:id])
26
+
27
+ if @request.nil? || @request.completed?
28
+ flash[:error] = t('workarea.storefront.review_requests.flash_messages.already_submitted')
29
+ redirect_to(root_path) && (return)
30
+ end
31
+ end
32
+
33
+ def find_product
34
+ model = Catalog::Product.find(@request.product_id)
35
+ @product = ProductViewModel.wrap(model, view_model_options)
36
+ end
37
+
38
+ def set_review
39
+ @review = ReviewViewModel.wrap(Review.new(review_params))
40
+ end
41
+
42
+ def review_params
43
+ ReviewRequestParams.new(
44
+ @request,
45
+ params[:review]&.permit(:rating, :title, :body)&.to_h || {}
46
+ ).to_h
47
+ end
48
+ end
49
+ end
50
+ end
data/app/controllers/workarea/storefront/reviews_controller.rb ADDED
@@ -0,0 +1,53 @@
1
+ module Workarea
2
+ module Storefront
3
+ class ReviewsController < Storefront::ApplicationController
4
+ before_action :find_product
5
+ before_action :set_review
6
+
7
+ def new; end
8
+
9
+ def create
10
+ if review_can_be_saved? && @create_review.save
11
+ redirect_to product_path(params[:product_id])
12
+ flash[:success] = t('workarea.storefront.reviews.flash_messages.created')
13
+ else
14
+ render :new
15
+ flash[:error] = t('workarea.storefront.reviews.flash_messages.failure')
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def set_review
22
+ @create_review = CreateReview.for(
23
+ product: @product,
24
+ user: current_user,
25
+ params: review_params.to_h
26
+ )
27
+
28
+ @review = ReviewViewModel.wrap(@create_review.review)
29
+ end
30
+
31
+ def find_product
32
+ @product ||=
33
+ begin
34
+ model = Catalog::Product.find_by(slug: params[:product_id])
35
+ raise InvalidDisplay unless model.active? || current_user.try(:admin?)
36
+ ProductViewModel.wrap(model)
37
+ end
38
+ end
39
+
40
+ def review_params
41
+ return {} unless params[:review].present?
42
+
43
+ params[:review].permit(
44
+ :product_id, :rating, :title, :body, :first_name, :last_name, :email
45
+ )
46
+ end
47
+
48
+ def review_can_be_saved?
49
+ verify_recaptcha(model: @create_review, env: Rails.env)
50
+ end
51
+ end
52
+ end
53
+ end
data/app/helpers/workarea/admin/reviews_helper.rb ADDED
@@ -0,0 +1,13 @@
1
+ module Workarea
2
+ module Admin
3
+ module ReviewsHelper
4
+ def reviews_report_filter_options
5
+ [
6
+ [t('workarea.admin.reports.reviews_by_product.filters.all'), nil],
7
+ [t('workarea.admin.reports.reviews_by_product.filters.approved'), 'approved'],
8
+ [t('workarea.admin.reports.reviews_by_product.filters.unapproved'), 'unapproved']
9
+ ]
10
+ end
11
+ end
12
+ end
13
+ end
data/app/helpers/workarea/storefront/reviews_helper.rb ADDED
@@ -0,0 +1,22 @@
1
+ module Workarea
2
+ module Storefront
3
+ module ReviewsHelper
4
+ def rating_stars(rating, options = {})
5
+ full_star_count = rating.floor
6
+ empty_star_count = 5 - rating.ceil
7
+ half_star_size = (rating % 1).round(2) * 100
8
+ half_star_width = 20 + (half_star_size - 0) * (80.0 - 20) / (100.0 - 0)
9
+
10
+ render 'workarea/storefront/products/rating', rating: rating, full_star_count: full_star_count, empty_star_count: empty_star_count, half_star_width: half_star_width, half_star_size: half_star_size
11
+ end
12
+
13
+ def display_purchase_requirement_message
14
+ if current_user && Workarea.config.require_purchase_to_post_review && current_user.total_spent.to_f.zero?
15
+ true
16
+ else
17
+ false
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
data/app/helpers/workarea/storefront/reviews_schema_org_helper.rb ADDED
@@ -0,0 +1,34 @@
1
+ module Workarea
2
+ module Storefront
3
+ module ReviewsSchemaOrgHelper
4
+ def product_schema(product, related_products: nil)
5
+ schema = super.merge(
6
+ 'review': product.reviews.map do |review|
7
+ {
8
+ '@type': 'Review',
9
+ 'author': review.user_info,
10
+ 'datePublished': review.created_at.strftime('%Y-%m-%d'),
11
+ 'description': review.body,
12
+ 'name': review.title,
13
+ 'reviewRating': {
14
+ '@type': 'Rating',
15
+ 'bestRating': '5',
16
+ 'worstRating': '1',
17
+ 'ratingValue': review.rating.round(2).to_s
18
+ }
19
+ }
20
+ end
21
+ )
22
+
23
+ if product.total_reviews > 0 && product.average_rating.present?
24
+ schema['aggregateRating'] = {
25
+ 'reviewCount': product.total_reviews.to_s,
26
+ 'ratingValue': product.average_rating.round(2).to_s
27
+ }
28
+ end
29
+
30
+ schema
31
+ end
32
+ end
33
+ end
34
+ end
data/app/mailers/workarea/admin/status_report_mailer.decorator ADDED
@@ -0,0 +1,8 @@
1
+ module Workarea
2
+ decorate Admin::StatusReportMailer, with: :reviews do
3
+ def report(*)
4
+ @reviews_summary = ReviewSummary.new
5
+ super
6
+ end
7
+ end
8
+ end
data/app/mailers/workarea/storefront/review_mailer.rb ADDED
@@ -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
data/app/models/workarea/catalog/product.decorator ADDED
@@ -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
data/app/models/workarea/fulfillment.decorator ADDED
@@ -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
data/app/models/workarea/insights/most_active_reviewers.rb ADDED
@@ -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
data/app/models/workarea/insights/most_reviewed_products.rb ADDED
@@ -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
data/app/models/workarea/insights/top_rated_products.rb ADDED
@@ -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
data/app/models/workarea/review.rb ADDED
@@ -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
data/app/models/workarea/review/request.rb ADDED
@@ -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
data/app/models/workarea/search/admin/review.rb ADDED
@@ -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')