workarea-reviews 3.0.8 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.eslintrc.json +35 -0
- data/.github/workflows/ci.yml +60 -0
- data/.gitignore +1 -2
- data/.rubocop.yml +3 -0
- data/.stylelintrc.json +8 -0
- data/CHANGELOG.md +24 -12
- data/Gemfile +4 -2
- data/Rakefile +4 -5
- data/app/controllers/workarea/admin/reports_controller.decorator +10 -0
- data/app/helpers/workarea/admin/reviews_helper.rb +6 -5
- data/app/helpers/workarea/storefront/reviews_helper.rb +1 -3
- data/app/helpers/workarea/storefront/reviews_schema_org_helper.rb +34 -0
- data/app/mailers/workarea/admin/status_report_mailer.decorator +2 -2
- data/app/models/workarea/insights/most_active_reviewers.rb +34 -0
- data/app/models/workarea/insights/most_reviewed_products.rb +33 -0
- data/app/models/workarea/insights/top_rated_products.rb +33 -0
- data/app/models/workarea/review.rb +5 -16
- data/app/queries/workarea/reports/reviews_by_product.rb +86 -0
- data/app/queries/workarea/reports/reviews_by_user.rb +71 -0
- data/app/seeds/{reviews.rb → workarea/review_seeds.rb} +17 -6
- data/app/view_models/workarea/admin/dashboards/reports_view_model.decorator +11 -0
- data/app/view_models/workarea/admin/reports/reviews_by_product_view_model.rb +27 -0
- data/app/view_models/workarea/storefront/review_view_model.rb +9 -3
- data/app/views/workarea/admin/dashboards/_reviews_by_product_card.html.haml +17 -0
- data/app/views/workarea/admin/insights/_most_active_reviewers.html.haml +22 -0
- data/app/views/workarea/admin/insights/_most_reviewed_products.html.haml +22 -0
- data/app/views/workarea/admin/insights/_top_rated_products.html.haml +22 -0
- data/app/views/workarea/admin/reports/reviews_by_product.html.haml +44 -0
- data/app/views/workarea/storefront/products/_rating.html.haml +1 -2
- data/app/views/workarea/storefront/products/_reviews.html.haml +5 -5
- data/app/views/workarea/storefront/products/_reviews_aggregate.html.haml +2 -2
- data/app/views/workarea/storefront/review_mailer/review_request.html.haml +2 -0
- data/app/views/workarea/storefront/review_requests/show.html.haml +2 -1
- data/config/initializers/append_points.rb +5 -0
- data/config/initializers/configuration.rb +3 -1
- data/config/initializers/scheduled_jobs.rb +8 -6
- data/config/locales/en.yml +46 -0
- data/config/routes.rb +4 -0
- data/lib/workarea/reviews/engine.rb +4 -0
- data/lib/workarea/reviews/version.rb +1 -1
- data/package.json +9 -0
- data/test/dummy/config/initializers/session_store.rb +3 -1
- data/test/helpers/workarea/storefront/reviews_helper_test.rb +1 -1
- data/test/integration/workarea/admin/reviews_integration_test.rb +5 -1
- data/test/models/workarea/insights/most_active_reviewers_test.rb +32 -0
- data/test/models/workarea/insights/most_reviewed_products_test.rb +32 -0
- data/test/models/workarea/insights/top_rated_products_test.rb +49 -0
- data/test/models/workarea/review_test.rb +29 -18
- data/test/queries/workarea/reports/reviews_by_product_test.rb +104 -0
- data/test/queries/workarea/reports/reviews_by_user_test.rb +102 -0
- data/test/system/workarea/admin/reviews_by_product_system_test.rb +60 -0
- data/test/view_models/workarea/storefront/review_view_model_test.rb +46 -0
- data/workarea-reviews.gemspec +1 -1
- data/yarn.lock +3265 -0
- metadata +33 -8
- data/app/controllers/workarea/admin/import_reviews_controller.rb +0 -35
- data/test/helpers/workarea/admin/reviews_helper_test.rb +0 -17
| @@ -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
         | 
| @@ -3,17 +3,26 @@ module Workarea | |
| 3 3 | 
             
                def perform
         | 
| 4 4 | 
             
                  puts 'Adding reviews...'
         | 
| 5 5 |  | 
| 6 | 
            -
                   | 
| 7 | 
            -
                     | 
| 6 | 
            +
                  Sidekiq::Callbacks.disable do
         | 
| 7 | 
            +
                    create_reviews_for_catalog
         | 
| 8 | 
            +
                    create_review_request_email_content
         | 
| 8 9 | 
             
                  end
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                  create_review_request_email_content
         | 
| 11 10 | 
             
                end
         | 
| 12 11 |  | 
| 13 12 | 
             
                private
         | 
| 14 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 | 
            +
             | 
| 15 24 | 
             
                def create_review(product)
         | 
| 16 | 
            -
                  Workarea::Review. | 
| 25 | 
            +
                  Workarea::Review.new(
         | 
| 17 26 | 
             
                    product_id: product.id,
         | 
| 18 27 | 
             
                    user_id: BSON::ObjectId.new,
         | 
| 19 28 | 
             
                    rating: rand(5) + 1,
         | 
| @@ -21,7 +30,9 @@ module Workarea | |
| 21 30 | 
             
                    body: Faker::Lorem.paragraph,
         | 
| 22 31 | 
             
                    approved: [true, false].sample,
         | 
| 23 32 | 
             
                    user_info: Faker::Internet.user_name,
         | 
| 24 | 
            -
                    verified: [true, false].sample
         | 
| 33 | 
            +
                    verified: [true, false].sample,
         | 
| 34 | 
            +
                    created_at: rand(45).days.ago,
         | 
| 35 | 
            +
                    updated_at: Time.current
         | 
| 25 36 | 
             
                  )
         | 
| 26 37 | 
             
                end
         | 
| 27 38 |  | 
| @@ -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,27 @@ | |
| 1 | 
            +
            module Workarea
         | 
| 2 | 
            +
              module Admin
         | 
| 3 | 
            +
                module Reports
         | 
| 4 | 
            +
                  class ReviewsByProductViewModel < ApplicationViewModel
         | 
| 5 | 
            +
                    def results
         | 
| 6 | 
            +
                      @results ||= model.results.map do |result|
         | 
| 7 | 
            +
                        product = products.detect { |p| p.id == result['_id'] }
         | 
| 8 | 
            +
                        OpenStruct.new(
         | 
| 9 | 
            +
                          { product: product }
         | 
| 10 | 
            +
                            .merge(result)
         | 
| 11 | 
            +
                            .merge(
         | 
| 12 | 
            +
                              average_rating: result['average_rating'].round(2),
         | 
| 13 | 
            +
                              weighted_average_rating: result['weighted_average_rating'].round(2)
         | 
| 14 | 
            +
                            )
         | 
| 15 | 
            +
                        )
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    def products
         | 
| 20 | 
            +
                      @products ||= Catalog::Product.any_in(
         | 
| 21 | 
            +
                        id: model.results.map { |r| r['_id'] }
         | 
| 22 | 
            +
                      ).to_a
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
| @@ -2,12 +2,18 @@ module Workarea | |
| 2 2 | 
             
              module Storefront
         | 
| 3 3 | 
             
                class ReviewViewModel < ApplicationViewModel
         | 
| 4 4 | 
             
                  def requires_public_info?
         | 
| 5 | 
            -
                    anonymous? || user | 
| 5 | 
            +
                    anonymous? || user&.public_info.blank?
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def anonymous?
         | 
| 9 | 
            +
                    model.anonymous? || user.nil?
         | 
| 6 10 | 
             
                  end
         | 
| 7 11 |  | 
| 8 12 | 
             
                  def user
         | 
| 9 | 
            -
                    return if anonymous?
         | 
| 10 | 
            -
                    @user  | 
| 13 | 
            +
                    return if model.anonymous?
         | 
| 14 | 
            +
                    return @user if defined?(@user)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    @user = User.find(user_id) rescue nil
         | 
| 11 17 | 
             
                  end
         | 
| 12 18 | 
             
                end
         | 
| 13 19 | 
             
              end
         | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            .grid__cell
         | 
| 2 | 
            +
              .card{ class: card_classes(:reviews_by_product_report, local_assigns[:active]) }
         | 
| 3 | 
            +
                = link_to reviews_by_product_report_path, class: 'card__header' do
         | 
| 4 | 
            +
                  %span.card__header-text= t('workarea.admin.reports.reviews_by_product.title')
         | 
| 5 | 
            +
                  = inline_svg 'workarea/admin/icons/insights.svg', class: 'card__icon'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                .card__body
         | 
| 8 | 
            +
                  .card__centered-content
         | 
| 9 | 
            +
                    %table
         | 
| 10 | 
            +
                      %tbody
         | 
| 11 | 
            +
                        - @dashboard.reviews_by_product.results.take(4).each do |result|
         | 
| 12 | 
            +
                          %tr
         | 
| 13 | 
            +
                            %td= result.product.present? ? result.product.name : result._id
         | 
| 14 | 
            +
                            %td.align-right= number_with_delimiter result.reviews
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    = link_to reviews_by_product_report_path, class: 'card__button' do
         | 
| 17 | 
            +
                      %span.button.button--small= t('workarea.admin.dashboards.reports.view_full_report')
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            .insight
         | 
| 2 | 
            +
              .insight__date
         | 
| 3 | 
            +
                %span.insight__period= insight.reporting_on.strftime('%B %Y')
         | 
| 4 | 
            +
              .insight__heading= t('workarea.admin.insights.most_active_reviewers.title')
         | 
| 5 | 
            +
              .insight__body
         | 
| 6 | 
            +
                %p.insight__note= t('workarea.admin.insights.most_active_reviewers.info')
         | 
| 7 | 
            +
                %table
         | 
| 8 | 
            +
                  %thead
         | 
| 9 | 
            +
                    %tr
         | 
| 10 | 
            +
                      %th.align-center= t('workarea.admin.insights.most_active_reviewers.user')
         | 
| 11 | 
            +
                      %th.align-center= t('workarea.admin.insights.most_active_reviewers.reviews')
         | 
| 12 | 
            +
                      %th.align-center= t('workarea.admin.insights.most_active_reviewers.verified')
         | 
| 13 | 
            +
                  %tbody
         | 
| 14 | 
            +
                    - insight.results.each do |result|
         | 
| 15 | 
            +
                      %tr
         | 
| 16 | 
            +
                        %td.align-center
         | 
| 17 | 
            +
                          - if result.user.present?
         | 
| 18 | 
            +
                            = link_to result.user.name, user_path(result.user)
         | 
| 19 | 
            +
                          - else
         | 
| 20 | 
            +
                            = result.email
         | 
| 21 | 
            +
                        %td.align-center= number_with_delimiter result.reviews
         | 
| 22 | 
            +
                        %td.align-center= number_with_delimiter result.verified
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            .insight
         | 
| 2 | 
            +
              .insight__date
         | 
| 3 | 
            +
                %span.insight__period= insight.reporting_on.strftime('%B %Y')
         | 
| 4 | 
            +
              .insight__heading= t('workarea.admin.insights.most_reviewed_products.title')
         | 
| 5 | 
            +
              .insight__body
         | 
| 6 | 
            +
                %p.insight__note= t('workarea.admin.insights.most_reviewed_products.info')
         | 
| 7 | 
            +
                .grid.grid--large.grid--center
         | 
| 8 | 
            +
                  - insight.results.each do |result|
         | 
| 9 | 
            +
                    .grid__cell.grid__cell--50.grid__cell--25-at-wide.align-center
         | 
| 10 | 
            +
                      .insight__product
         | 
| 11 | 
            +
                        - if result.product.blank?
         | 
| 12 | 
            +
                          = image_tag(product_image_url(Workarea::Catalog::ProductPlaceholderImage.cached, :small), alt: result.product_id, class: 'insight__product-image')
         | 
| 13 | 
            +
                        - else
         | 
| 14 | 
            +
                          = link_to catalog_product_path(result.product) do
         | 
| 15 | 
            +
                            = image_tag(product_image_url(result.product.primary_image, :small), alt: result.product.name, class: 'insight__product-image')
         | 
| 16 | 
            +
                        .insight__product-name
         | 
| 17 | 
            +
                          - if result.product.blank?
         | 
| 18 | 
            +
                            = result.product_id
         | 
| 19 | 
            +
                          - else
         | 
| 20 | 
            +
                            = link_to result.product.name, catalog_product_path(result.product)
         | 
| 21 | 
            +
                        .insight__product-info
         | 
| 22 | 
            +
                          %strong= t('workarea.admin.insights.most_reviewed_products.reviews', count: result.reviews)
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            .insight
         | 
| 2 | 
            +
              .insight__date
         | 
| 3 | 
            +
                %span.insight__period= insight.reporting_on.strftime('%B %Y')
         | 
| 4 | 
            +
              .insight__heading= t('workarea.admin.insights.top_rated_products.title')
         | 
| 5 | 
            +
              .insight__body
         | 
| 6 | 
            +
                %p.insight__note= t('workarea.admin.insights.top_rated_products.info')
         | 
| 7 | 
            +
                .grid.grid--large.grid--center
         | 
| 8 | 
            +
                  - insight.results.each do |result|
         | 
| 9 | 
            +
                    .grid__cell.grid__cell--50.grid__cell--25-at-wide.align-center
         | 
| 10 | 
            +
                      .insight__product
         | 
| 11 | 
            +
                        - if result.product.blank?
         | 
| 12 | 
            +
                          = image_tag(product_image_url(Workarea::Catalog::ProductPlaceholderImage.cached, :small), alt: result.product_id, class: 'insight__product-image')
         | 
| 13 | 
            +
                        - else
         | 
| 14 | 
            +
                          = link_to catalog_product_path(result.product) do
         | 
| 15 | 
            +
                            = image_tag(product_image_url(result.product.primary_image, :small), alt: result.product.name, class: 'insight__product-image')
         | 
| 16 | 
            +
                        .insight__product-name
         | 
| 17 | 
            +
                          - if result.product.blank?
         | 
| 18 | 
            +
                            = result.product_id
         | 
| 19 | 
            +
                          - else
         | 
| 20 | 
            +
                            = link_to result.product.name, catalog_product_path(result.product)
         | 
| 21 | 
            +
                        .insight__product-info
         | 
| 22 | 
            +
                          %strong= t('workarea.admin.insights.top_rated_products.rating', rating: number_with_delimiter(result.weighted_average_rating.round(2)))
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            - @page_title = t('workarea.admin.reports.reviews_by_product.title')
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            .view
         | 
| 4 | 
            +
              .view__header
         | 
| 5 | 
            +
                .view__heading
         | 
| 6 | 
            +
                  = link_to "↑ #{t('workarea.admin.reports.all_reports')}", reports_dashboards_path
         | 
| 7 | 
            +
                  %h1.heading.heading--no-margin= t('workarea.admin.reports.reviews_by_product.title')
         | 
| 8 | 
            +
                  %p= t('workarea.admin.reports.reference_link_html', path: reference_report_path)
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              .view__container.view__container--narrow
         | 
| 11 | 
            +
                .browsing-controls.browsing-controls--with-divider.browsing-controls--center.browsing-controls--filters-displayed
         | 
| 12 | 
            +
                  = form_tag reviews_by_product_report_path, method: 'get', class: 'browsing-controls__form' do
         | 
| 13 | 
            +
                    = render 'workarea/admin/shared/date_selector', starts_at: @report.starts_at, ends_at: @report.ends_at
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    .browsing-controls__filter
         | 
| 16 | 
            +
                      .property.property--inline
         | 
| 17 | 
            +
                        = label_tag 'results_filter', t('workarea.admin.reports.reviews_by_product.results_filter'), class: 'property__name'
         | 
| 18 | 
            +
                        = select_tag 'results_filter', options_for_select(reviews_report_filter_options, params[:results_filter]), data: { form_submitting_control: '' }
         | 
| 19 | 
            +
             | 
| 20 | 
            +
             | 
| 21 | 
            +
                  .browsing-controls__count
         | 
| 22 | 
            +
                    = render_reports_results_message(@report)
         | 
| 23 | 
            +
                    = render 'workarea/admin/reports/export', report: @report
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                %table
         | 
| 26 | 
            +
                  %thead
         | 
| 27 | 
            +
                    %tr
         | 
| 28 | 
            +
                      %th= t('workarea.admin.fields.product')
         | 
| 29 | 
            +
                      %th.align-center= link_to_reports_sorting t('workarea.admin.fields.reviews'), report: @report, sort_by: 'reviews'
         | 
| 30 | 
            +
                      %th.align-center= link_to_reports_sorting t('workarea.admin.fields.verified_reviews'), report: @report, sort_by: 'verified'
         | 
| 31 | 
            +
                      %th.align-center= link_to_reports_sorting t('workarea.admin.fields.average_rating'), report: @report, sort_by: 'average_rating'
         | 
| 32 | 
            +
                      %th.align-center= link_to_reports_sorting t('workarea.admin.fields.weighted_average_rating'), report: @report, sort_by: 'weighted_average_rating'
         | 
| 33 | 
            +
                  %tbody
         | 
| 34 | 
            +
                    - @report.results.each do |result|
         | 
| 35 | 
            +
                      %tr
         | 
| 36 | 
            +
                        %td
         | 
| 37 | 
            +
                          - if result.product.present?
         | 
| 38 | 
            +
                            = link_to result.product.name, catalog_product_path(result.product)
         | 
| 39 | 
            +
                          - else
         | 
| 40 | 
            +
                            = result._id
         | 
| 41 | 
            +
                        %td.align-center= number_with_delimiter result.reviews
         | 
| 42 | 
            +
                        %td.align-center= number_with_delimiter result.verified
         | 
| 43 | 
            +
                        %td.align-center= number_with_delimiter result.average_rating
         | 
| 44 | 
            +
                        %td.align-center= number_with_delimiter result.weighted_average_rating
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            %p.rating{ title: "#{rating.round(2)} #{t('workarea.storefront.reviews.out_of')} #{t('workarea.storefront.reviews.stars', count: 5)}" | 
| 1 | 
            +
            %p.rating{ title: "#{rating.round(2)} #{t('workarea.storefront.reviews.out_of')} #{t('workarea.storefront.reviews.stars', count: 5)}" }
         | 
| 2 2 | 
             
              - full_star_count.times do
         | 
| 3 3 | 
             
                = inline_svg 'workarea/storefront/icons/star.svg', class: 'rating__star'
         | 
| 4 4 | 
             
              - if half_star_size > 0
         | 
| @@ -6,5 +6,4 @@ | |
| 6 6 | 
             
              - empty_star_count.times do
         | 
| 7 7 | 
             
                = inline_svg 'workarea/storefront/icons/empty_star.svg', class: 'rating__star'
         | 
| 8 8 | 
             
              %span.rating__text.visually-hidden
         | 
| 9 | 
            -
                %meta{ itemprop: 'ratingValue', content: rating.round(2) }
         | 
| 10 9 | 
             
                #{rating.round(2)} #{t('workarea.storefront.reviews.out_of')} #{t('workarea.storefront.reviews.stars', count: 5)}
         | 
| @@ -10,14 +10,14 @@ | |
| 10 10 |  | 
| 11 11 | 
             
                %ol.reviews__review-group
         | 
| 12 12 | 
             
                  - product.reviews.each do |review|
         | 
| 13 | 
            -
                    %li.reviews__review{  | 
| 13 | 
            +
                    %li.reviews__review{ data: { product_review_section_entry: { rating: review.rating.to_f, createdAt: review.created_at.to_i }.to_json } }
         | 
| 14 14 | 
             
                      = rating_stars(review.rating)
         | 
| 15 15 |  | 
| 16 | 
            -
                      %h3.reviews__review-title | 
| 17 | 
            -
                      %p.reviews__review-body | 
| 16 | 
            +
                      %h3.reviews__review-title= review.title
         | 
| 17 | 
            +
                      %p.reviews__review-body= review.body
         | 
| 18 18 | 
             
                      .reviews__review-meta
         | 
| 19 | 
            -
                        %p.reviews__review-author | 
| 20 | 
            -
                          %span | 
| 19 | 
            +
                        %p.reviews__review-author
         | 
| 20 | 
            +
                          %span= review.user_info
         | 
| 21 21 | 
             
                        - if review.verified?
         | 
| 22 22 | 
             
                          %p.reviews__review-verified= t('workarea.storefront.reviews.verified_purchaser')
         | 
| 23 23 | 
             
                        %p.reviews__review-date= local_time(review.created_at, format: :long, itemprop: 'datePublished')
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            - if product.has_reviews?
         | 
| 2 | 
            -
              .reviews-aggregate | 
| 2 | 
            +
              .reviews-aggregate
         | 
| 3 3 | 
             
                = link_to "#{product_path(product, product.browse_link_options)}#reviews", data: { scroll_to_button: '' }, class: 'reviews-aggregate__rating-link' do
         | 
| 4 4 | 
             
                  = rating_stars(product.average_rating, aggregate: true)
         | 
| 5 5 |  | 
| @@ -7,7 +7,7 @@ | |
| 7 7 | 
             
                  %span.reviews-aggregate__count
         | 
| 8 8 | 
             
                    %span.reviews-aggregate__read= t('workarea.storefront.reviews.read_reviews')
         | 
| 9 9 | 
             
                    = surround '(', ')' do
         | 
| 10 | 
            -
                      %span | 
| 10 | 
            +
                      %span= product.total_reviews
         | 
| 11 11 |  | 
| 12 12 | 
             
                = link_to t('workarea.storefront.reviews.write_a_review'), new_product_review_path(product), class: 'reviews-aggregate__write-action', data: { dialog_button: '' }
         | 
| 13 13 | 
             
                - if display_purchase_requirement_message
         | 
| @@ -5,7 +5,8 @@ | |
| 5 5 |  | 
| 6 6 | 
             
              .grid
         | 
| 7 7 | 
             
                .grid__cell.grid__cell--25
         | 
| 8 | 
            -
                  .product-summary | 
| 8 | 
            +
                  .product-summary
         | 
| 9 | 
            +
                    = render_schema_org(product_schema(@product))
         | 
| 9 10 | 
             
                    = render 'workarea/storefront/products/summary', product: @product
         | 
| 10 11 |  | 
| 11 12 | 
             
                .grid__cell.grid__cell--75
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            Workarea.configure do |config|
         | 
| 2 | 
            -
              config.seeds  | 
| 2 | 
            +
              config.seeds.insert('Workarea::InsightsSeeds', 'Workarea::ReviewSeeds')
         | 
| 3 3 |  | 
| 4 4 | 
             
              config.jump_to_navigation.merge!('Product Reviews' => :reviews_path)
         | 
| 5 5 |  | 
| @@ -11,6 +11,8 @@ Workarea.configure do |config| | |
| 11 11 |  | 
| 12 12 | 
             
              config.data_file_ignored_fields << %w(total_reviews average_rating)
         | 
| 13 13 |  | 
| 14 | 
            +
              config.insights_model_classes << 'Workarea::User'
         | 
| 15 | 
            +
             | 
| 14 16 | 
             
              # The amount of time before a Review::Request will auto expire
         | 
| 15 17 | 
             
              # and be removed from the collection
         | 
| 16 18 | 
             
              config.review_request_ttl = 6.months
         | 
| @@ -1,6 +1,8 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
               | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 1 | 
            +
            unless Workarea.config.skip_service_connections
         | 
| 2 | 
            +
              Sidekiq::Cron::Job.create(
         | 
| 3 | 
            +
                name: 'Workarea::SendReviewRequests',
         | 
| 4 | 
            +
                klass: 'Workarea::SendReviewRequests',
         | 
| 5 | 
            +
                cron: "0 0 * * * #{Time.zone.tzinfo.identifier}",
         | 
| 6 | 
            +
                queue: 'low'
         | 
| 7 | 
            +
              )
         | 
| 8 | 
            +
            end
         | 
    
        data/config/locales/en.yml
    CHANGED
    
    | @@ -15,12 +15,55 @@ en: | |
| 15 15 | 
             
                    marketing:
         | 
| 16 16 | 
             
                      reviews: Product reviews
         | 
| 17 17 | 
             
                  fields:
         | 
| 18 | 
            +
                    average_rating: Average Rating
         | 
| 18 19 | 
             
                    body: Body
         | 
| 19 20 | 
             
                    title: Title
         | 
| 20 21 | 
             
                    rating: Rating
         | 
| 22 | 
            +
                    reviews: Reviews
         | 
| 21 23 | 
             
                    approved: Approved
         | 
| 22 24 | 
             
                    auto_approve: Approve All Reviews
         | 
| 23 25 | 
             
                    verified: Verified Purchaser
         | 
| 26 | 
            +
                    verified_reviews: Verified
         | 
| 27 | 
            +
                    weighted_average_rating: Weighted Average Rating
         | 
| 28 | 
            +
                  insights:
         | 
| 29 | 
            +
                    most_active_reviewers:
         | 
| 30 | 
            +
                      info: Your customers that are submitting the most reviews.
         | 
| 31 | 
            +
                      reviews: Reviews
         | 
| 32 | 
            +
                      title: Most Active Reviewers
         | 
| 33 | 
            +
                      user: User
         | 
| 34 | 
            +
                      verified: Verified Reviews
         | 
| 35 | 
            +
                    most_reviewed_products:
         | 
| 36 | 
            +
                      title: Most Reviewed Products
         | 
| 37 | 
            +
                      info: Your products that have received the most reviews.
         | 
| 38 | 
            +
                      reviews:
         | 
| 39 | 
            +
                        one: '%{count} Review'
         | 
| 40 | 
            +
                        other: '%{count} Reviews'
         | 
| 41 | 
            +
                    top_rated_products:
         | 
| 42 | 
            +
                      title: Top Rated Products
         | 
| 43 | 
            +
                      info: Your products with the highest weighted average rating.
         | 
| 44 | 
            +
                      rating: '%{rating} Rating'
         | 
| 45 | 
            +
                  reports:
         | 
| 46 | 
            +
                    reference:
         | 
| 47 | 
            +
                      terms:
         | 
| 48 | 
            +
                        average_rating:
         | 
| 49 | 
            +
                          name: Average Rating
         | 
| 50 | 
            +
                          description: The sum of all review ratings divided by the total number of reviews.
         | 
| 51 | 
            +
                        reviews:
         | 
| 52 | 
            +
                          name: Reviews
         | 
| 53 | 
            +
                          description: The total number of reviews.
         | 
| 54 | 
            +
                        verified:
         | 
| 55 | 
            +
                          name: Verified
         | 
| 56 | 
            +
                          description: The total number of reviews made by customers who have purchased the product.
         | 
| 57 | 
            +
                        weighted_average_rating:
         | 
| 58 | 
            +
                          name: Weighted Average Rating
         | 
| 59 | 
            +
                          description: A base rating of 3.0, adjusted up or down by each review rating.
         | 
| 60 | 
            +
                    reviews_by_product:
         | 
| 61 | 
            +
                      filters:
         | 
| 62 | 
            +
                        all: All Reviews
         | 
| 63 | 
            +
                        approved: Approved Reviews
         | 
| 64 | 
            +
                        unapproved: Unapproved Reviews
         | 
| 65 | 
            +
                      results_filter: Results Filter
         | 
| 66 | 
            +
                      title: Reviews By Product
         | 
| 24 67 | 
             
                  reviews:
         | 
| 25 68 | 
             
                    approved: Approved
         | 
| 26 69 | 
             
                    catalog_products_cards:
         | 
| @@ -66,6 +109,9 @@ en: | |
| 66 109 | 
             
                    review_request:
         | 
| 67 110 | 
             
                      subject: Review your recent purchase of %{product}
         | 
| 68 111 | 
             
                      link: Write a review
         | 
| 112 | 
            +
                      email_action:
         | 
| 113 | 
            +
                        name: Write Review
         | 
| 114 | 
            +
                        description: Please review your recent purchase
         | 
| 69 115 | 
             
                  fields:
         | 
| 70 116 | 
             
                    username: Username
         | 
| 71 117 | 
             
                  review_requests:
         |