solidus_favorites 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +26 -0
  3. data/README.md +53 -0
  4. data/Rakefile +23 -0
  5. data/app/assets/javascripts/spree/backend/solidus_favorites.js +2 -0
  6. data/app/assets/javascripts/spree/frontend/solidus_favorites.js +40 -0
  7. data/app/assets/stylesheets/spree/backend/solidus_favorites.css +4 -0
  8. data/app/assets/stylesheets/spree/frontend/solidus_favorites.css +28 -0
  9. data/app/controllers/spree/admin/favorites_controller.rb +16 -0
  10. data/app/controllers/spree/favorites_controller.rb +51 -0
  11. data/app/helpers/solidus_favorites/store_helper_decorator.rb +27 -0
  12. data/app/helpers/spree/store_helper_decorator.rb +1 -0
  13. data/app/models/solidus_favorites/product_decorator.rb +21 -0
  14. data/app/models/solidus_favorites/user_decorator.rb +17 -0
  15. data/app/models/spree/favorite.rb +21 -0
  16. data/app/models/spree/product_decorator.rb +1 -0
  17. data/app/models/spree/user_decorator.rb +1 -0
  18. data/app/overrides/add_favorite_link_to_product_show.rb +6 -0
  19. data/app/overrides/add_favorite_to_admin_product_sidebar.rb +6 -0
  20. data/app/views/spree/admin/favorites/index.html.erb +45 -0
  21. data/app/views/spree/admin/favorites/users.html.erb +35 -0
  22. data/app/views/spree/favorites/_product_link.html.erb +24 -0
  23. data/app/views/spree/favorites/create.js.erb +6 -0
  24. data/app/views/spree/favorites/destroy.js.erb +10 -0
  25. data/app/views/spree/favorites/index.html.erb +52 -0
  26. data/config/locales/en.yml +25 -0
  27. data/config/routes.rb +9 -0
  28. data/db/migrate/20190624204433_create_table_spree_favorites.rb +15 -0
  29. data/lib/generators/solidus_favorites/install/install_generator.rb +30 -0
  30. data/lib/solidus_favorites.rb +7 -0
  31. data/lib/solidus_favorites/configuration.rb +7 -0
  32. data/lib/solidus_favorites/engine.rb +27 -0
  33. data/lib/solidus_favorites/factories.rb +6 -0
  34. data/lib/solidus_favorites/version.rb +3 -0
  35. metadata +354 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9ca056d414e6a414422b6bdc2db2b2b8b35b8546f3cac850334af16a622344d8
4
+ data.tar.gz: 35762cf6e3b11d9ff76ab143f9c361401faabb56c3f1c3bba4b01fe6bdd5761d
5
+ SHA512:
6
+ metadata.gz: 490e88878d3d1611aacdd588a208ec3451c1cab5a1d9733014c9a5f6c137ed55d83d2fabdb7473b013fba4a2e9675305980b401cdc71a8f2e837a68fcfc6efd6
7
+ data.tar.gz: 875dd5fa89544ed5f86d8bf4c3b37fad53cf34c08c77fcf09f971db4e7ec959df63fae1fcb036bd801b62f0635dd455d5efa988cb8995f43f7e673627f4bbdc0
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2019 [name of plugin creator]
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name Spree nor the names of its contributors may be used to
13
+ endorse or promote products derived from this software without specific
14
+ prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ SolidusFavorites
2
+ ================
3
+
4
+ [![Build Status](https://travis-ci.org/jtapia/solidus_favorites.svg?branch=master)](https://travis-ci.org/jtapia/solidus_favorites)
5
+
6
+ Solidus Favorite Products is an extension that allows the user to mark/unkmark a product as favorite from the product page.
7
+
8
+ - Admin can view which products have been marked as favorite, and by which users, on the Admin end.
9
+ - It gives user the ability to see all products marked as favorite by him/her.
10
+ - Allow user to add the products marked as favorite to cart.
11
+
12
+ Installation
13
+ ------------
14
+
15
+ Add `solidus_favorites` to your Gemfile:
16
+
17
+ ```ruby
18
+ gem 'solidus_favorites'
19
+ ```
20
+
21
+ Bundle your dependencies and run the installation generator:
22
+
23
+ ```shell
24
+ bundle
25
+ bundle exec rails g solidus_favorites:install
26
+ ```
27
+
28
+ Preview
29
+ ----------
30
+
31
+ ![](https://user-images.githubusercontent.com/957520/60153884-4d160000-97ab-11e9-8b82-6812cd6049e1.png)
32
+ ![](https://user-images.githubusercontent.com/957520/60153886-4d160000-97ab-11e9-95ed-659ab7bb2498.png)
33
+ ![](https://user-images.githubusercontent.com/957520/60153970-82225280-97ab-11e9-9358-c88500feb5ac.png)
34
+ ![](https://user-images.githubusercontent.com/957520/60153962-7c2c7180-97ab-11e9-8c43-95c1c0f247ad.png)
35
+
36
+ Testing
37
+ -------
38
+
39
+ First bundle your dependencies, then run `rake`. `rake` will default to building the dummy app if it does not exist, then it will run specs, and [Rubocop](https://github.com/bbatsov/rubocop) static code analysis. The dummy app can be regenerated by using `rake test_app`.
40
+
41
+ ```shell
42
+ bundle
43
+ bundle exec rake
44
+ ```
45
+
46
+ When testing your applications integration with this extension you may use it's factories.
47
+ Simply add this require statement to your spec_helper:
48
+
49
+ ```ruby
50
+ require 'solidus_favorites/factories'
51
+ ```
52
+
53
+ Copyright (c) 2019 [name of extension creator], released under the New BSD License
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ require 'rspec/core/rake_task'
7
+ require 'spree/testing_support/extension_rake'
8
+
9
+ RSpec::Core::RakeTask.new
10
+
11
+ task :default do
12
+ if Dir["spec/dummy"].empty?
13
+ Rake::Task[:test_app].invoke
14
+ Dir.chdir("../../")
15
+ end
16
+ Rake::Task[:spec].invoke
17
+ end
18
+
19
+ desc 'Generates a dummy app for testing'
20
+ task :test_app do
21
+ ENV['LIB_NAME'] = 'solidus_favorites'
22
+ Rake::Task['extension:test_app'].invoke
23
+ end
@@ -0,0 +1,2 @@
1
+ // Placeholder manifest file.
2
+ // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/backend/all.js'
@@ -0,0 +1,40 @@
1
+ Spree.ready(function() {
2
+ $('.favorite_product_link').on('click', function(e) {
3
+ e.preventDefault();
4
+
5
+ var $el = $(e.currentTarget);
6
+
7
+ if ($el.hasClass('fa-heart-o')) {
8
+ $el.removeClass('fa-heart-o').addClass('fa-heart');
9
+ } else {
10
+ $el.removeClass('fa-heart').addClass('fa-heart-o');
11
+ }
12
+ })
13
+
14
+ $('.favorite_add_to_cart_link').on('click', function(e) {
15
+ e.preventDefault();
16
+
17
+ var addToCartLink = $(e.currentTarget);
18
+ var removeFavoriteLink = $('.favorite_remove_link');
19
+ var el = $(e.currentTarget);
20
+ var variantID = $(e.currentTarget).parents('.favorite_item').data('variantId');
21
+ var actionsContent = $(e.currentTarget).parent();
22
+ var actionLinks = $(e.currentTarget).parents('.favorite-product-actions');
23
+
24
+ actionsContent.addClass('hidden');
25
+ actionLinks.append("<i class='fa fa-refresh fa-spin'></i>")
26
+
27
+ $.ajax({
28
+ method: 'POST',
29
+ url: '/orders/populate',
30
+ dataType: 'html',
31
+ data: 'variant_id=' + variantID + '&quantity=1',
32
+ success: function(data) { Spree.fetch_cart('/cart_link'); },
33
+ error: function(jqXHR, textStatus, errorThrown) {},
34
+ complete: function(data) {
35
+ actionsContent.removeClass('hidden');
36
+ actionLinks.find('.fa-refresh').remove();
37
+ }
38
+ })
39
+ })
40
+ })
@@ -0,0 +1,4 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
4
+ */
@@ -0,0 +1,28 @@
1
+ table.favorite-products .favorite-product-name p,
2
+ table.favorite-products .favorite-product-name span {
3
+ display: block;
4
+ }
5
+
6
+ table.favorite-products .favorite-product-name span {
7
+ font-style: italic;
8
+ }
9
+
10
+ .account-my-favorite-products table .favorite_add_to_cart_link,
11
+ .account-my-favorite-products table .favorite_remove_link,
12
+ .account-my-favorite-products table .favorite_go_to_product_link {
13
+ display: inline-block;
14
+ margin: 0 5px;
15
+ }
16
+
17
+ .account-my-favorite-products table .favorite-product-actions .fa-refresh {
18
+ font-size: 18px;
19
+ }
20
+
21
+ #product-description [data-hook='favorite'] {
22
+ margin-top: 5px;
23
+ }
24
+
25
+ #product-description .favorite_product_link.fa-heart,
26
+ #product-description .favorite_product_link.fa-heart-o {
27
+ font-size: 18px;
28
+ }
@@ -0,0 +1,16 @@
1
+ module Spree
2
+ module Admin
3
+ class FavoritesController < Spree::Admin::BaseController
4
+ def index
5
+ @favorites = Spree::Product.joins(:favorites).page(params[:page]).
6
+ per(SolidusFavorites::Config.favorites_per_page)
7
+ end
8
+
9
+ def users
10
+ @product = Spree::Product.where(id: params[:id]).first
11
+ @users = @product.favorite_users.page(params[:page]).
12
+ per(SolidusFavorites::Config.favorites_per_page)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,51 @@
1
+ module Spree
2
+ class FavoritesController < Spree::StoreController
3
+ before_action :find_favorite, only: [:destroy]
4
+
5
+ respond_to :html, :js
6
+
7
+ def index
8
+ @favorites = try_spree_current_user.favorites.page(params[:page]).
9
+ per(SolidusFavorites::Config.favorites_per_page) || []
10
+ end
11
+
12
+ def create
13
+ @favorite = try_spree_current_user.favorites.new(favorite_params)
14
+ if @favorite.save
15
+ flash.now[:success] = t('spree.successfully_added_favorite')
16
+ else
17
+ flash.now[:error] = @favorite.errors.full_messages.to_sentence
18
+ end
19
+
20
+ respond_to do |format|
21
+ format.js
22
+ end
23
+ end
24
+
25
+ def destroy
26
+ if @favorite&.destroy
27
+ flash.now[:success] = t('spree.favorite_successfully_removed')
28
+ else
29
+ flash.now[:error] = t('spree.favorite_unsuccessfully_removed')
30
+ end
31
+
32
+ respond_with do |format|
33
+ format.js
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def find_favorite
40
+ @favorite ||= try_spree_current_user.favorites.find(params[:id])
41
+ end
42
+
43
+ def favorite_params
44
+ params.permit(:favorable_id, :favorable_type, :variant_id)
45
+ end
46
+
47
+ def model_class
48
+ Spree::Favorite
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,27 @@
1
+ module SolidusFavorites
2
+ module StoreHelperDecorator
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def favorite_image(product)
7
+ if product.respond_to?(:gallery)
8
+ product&.gallery&.images&.first
9
+ else
10
+ product&.variant_images&.first
11
+ end
12
+ end
13
+
14
+ def favorite_count
15
+ try_spree_current_user.favorites.count || 0
16
+ end
17
+
18
+ def get_favorite(object)
19
+ try_spree_current_user.favorites.find_by_favorable_id_and_favorable_type(object.id, object.class.to_s)
20
+ end
21
+
22
+ def has_favorite?(object)
23
+ try_spree_current_user.has_favorite?(object) if try_spree_current_user
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1 @@
1
+ Spree::StoreHelper.include(SolidusFavorites::StoreHelperDecorator)
@@ -0,0 +1,21 @@
1
+ module SolidusFavorites
2
+ module ProductDecorator
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ prepend(InstanceMethods)
7
+ has_many :favorites, as: :favorable
8
+ has_many :favorite_users, through: :favorites, class_name: 'Spree::User', source: 'user'
9
+
10
+ def self.favorite
11
+ joins(:favorites).uniq
12
+ end
13
+ end
14
+
15
+ module InstanceMethods
16
+ def to_cart_params
17
+ {}
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module SolidusFavorites
2
+ module UserDecorator
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ prepend(InstanceMethods)
7
+ has_many :favorites, class_name: 'Spree::Favorite'
8
+ has_many :favorite_products, through: :favorites, class_name: 'Spree::Product', source: :favorable, source_type: 'Spree::Product'
9
+ end
10
+
11
+ module InstanceMethods
12
+ def has_favorite?(object)
13
+ favorites.exists?(favorable_id: object.id, favorable_type: object.class.to_s)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ module Spree
2
+ class Favorite < ActiveRecord::Base
3
+ FAVORABLES_ALLOWED = [
4
+ 'Spree::Product'
5
+ ]
6
+
7
+ belongs_to :favorable, polymorphic: true
8
+ belongs_to :user, class_name: Spree.user_class.to_s
9
+ belongs_to :variant, class_name: 'Spree::Variant'
10
+
11
+ validates :favorable, presence: true
12
+ validates :favorable_type, presence: true, inclusion: { in: FAVORABLES_ALLOWED }
13
+
14
+ validates :user_id, uniqueness: { allow_blank: true, message: 'Already added as favorite.', scope: [:favorable_id, :favorable_type] }
15
+ validates :guest_token, uniqueness: { allow_blank: true, message: 'Already added as favorite.', scope: [:favorable_id, :favorable_type] }
16
+
17
+ scope :by_guest_token, -> (token) { where(guest_token: token) }
18
+
19
+ default_scope { order('created_at DESC') }
20
+ end
21
+ end
@@ -0,0 +1 @@
1
+ Spree::Product.include(SolidusFavorites::ProductDecorator)
@@ -0,0 +1 @@
1
+ Spree.user_class.include(SolidusFavorites::UserDecorator)
@@ -0,0 +1,6 @@
1
+ Deface::Override.new(
2
+ virtual_path: 'spree/products/show',
3
+ name: 'add_favorite_link_to_products',
4
+ insert_after: "div[itemprop='description']",
5
+ partial: 'spree/favorites/product_link'
6
+ )
@@ -0,0 +1,6 @@
1
+ Deface::Override.new(
2
+ virtual_path: 'spree/admin/shared/_product_sub_menu',
3
+ name: 'add_favorite_products_tab',
4
+ insert_bottom: 'ul.admin-subnav',
5
+ text: '<%= tab :favorites %>'
6
+ )
@@ -0,0 +1,45 @@
1
+ <% admin_breadcrumb(link_to(plural_resource_name(Spree::Product), spree.admin_products_path)) %>
2
+ <% admin_breadcrumb(plural_resource_name(Spree::Favorite)) %>
3
+
4
+ <%= paginate @favorites, theme: 'solidus_admin' %>
5
+
6
+ <% if @favorites.any? %>
7
+ <table class='index' id='listing_products'>
8
+ <colgroup>
9
+ <col style='width: 15%;'>
10
+ <col style='width: 5%;'>
11
+ <col style='width: 50%;'>
12
+ <col style='width: 15%;'>
13
+ <col style='width: 15%;'>
14
+ </colgroup>
15
+
16
+ <thead>
17
+ <tr data-hook='admin_products_index_headers'>
18
+ <th><%= t('spree.sku') %></th>
19
+ <th colspan='2'><%= t('spree.name') %></th>
20
+ <th class='align-center'><%= t('spree.master_price') %></th>
21
+ <th class='align-center'><%= t('spree.favorite_users') %></th>
22
+ </tr>
23
+ </thead>
24
+
25
+ <tbody>
26
+ <% @favorites.each do |favorite| %>
27
+ <tr <%== "style='color: red;'" if favorite.deleted? %> id='<%= spree_dom_id favorite %>' data-hook='admin_favorites_index_rows'>
28
+ <td><%= favorite.sku %></td>
29
+ <td class='align-center'>
30
+ <%= link_to(render('spree/shared/image', image: favorite_image(favorite), size: :small, itemprop: "image"), admin_product_path(favorite), itemprop: 'url') %>
31
+ </td>
32
+ <td><%= link_to(favorite.try(:name), admin_product_path(favorite)) %></td>
33
+ <td class='align-center'><%= favorite&.display_price.to_html %></td>
34
+ <td class='align-center'><%= link_to favorite&.favorite_users.count, users_admin_favorite_path(:id => favorite.id) %></td>
35
+ </tr>
36
+ <% end %>
37
+ </tbody>
38
+ </table>
39
+ <% else %>
40
+ <div class='no-objects-found'>
41
+ <%= t('spree.no_results') %>
42
+ </div>
43
+ <% end %>
44
+
45
+ <%= paginate @favorites, theme: 'solidus_admin' %>
@@ -0,0 +1,35 @@
1
+ <% admin_breadcrumb(link_to(plural_resource_name(Spree::Product), spree.admin_products_path)) %>
2
+ <% admin_breadcrumb(link_to(plural_resource_name(Spree::Favorite), spree.admin_favorites_path)) %>
3
+ <% admin_breadcrumb(t('spree.users')) %>
4
+
5
+ <% content_for :page_title do %>
6
+ <%= t('spree.favorite_users') %>
7
+ <% end %>
8
+
9
+ <%= paginate @users, theme: 'solidus_admin' %>
10
+
11
+ <% if @users.any? %>
12
+ <table class='index' id='listing_favorite_products_users'>
13
+ <thead>
14
+ <tr data-hook='admin_favorite_users_index_headers'>
15
+ <th><%= t('spree.email') %></th>
16
+ </tr>
17
+ </thead>
18
+
19
+ <tbody>
20
+ <% @users.each do |user| %>
21
+ <tr <%== "style='color: red;'" if user.deleted? %> id='<%= spree_dom_id user %>' data-hook='admin_favorite_users_index_rows'>
22
+ <td>
23
+ <%= user.email %>
24
+ </td>
25
+ </tr>
26
+ <% end %>
27
+ </tbody>
28
+ </table>
29
+ <% else %>
30
+ <div class='no-objects-found'>
31
+ <%= t('spree.no_results') %>
32
+ </div>
33
+ <% end %>
34
+
35
+ <%= paginate @users, theme: 'solidus_admin' %>
@@ -0,0 +1,24 @@
1
+ <div itemprop='favorite' data-hook='favorite'>
2
+ <% if has_favorite?(@product) %>
3
+ <%= link_to_with_icon '',
4
+ t('spree.remove_favorite'),
5
+ favorite_path(get_favorite(@product),
6
+ favorable_id: @product.id,
7
+ favorable_type: 'Spree::Product'),
8
+ method: :delete,
9
+ no_text: true,
10
+ remote: true,
11
+ id: "favorable_product_#{@product.id}",
12
+ class: 'favorite_product_link fa fa-heart no-text with-tip' %>
13
+ <% else %>
14
+ <%= link_to_with_icon '',
15
+ t('spree.add_favorite'),
16
+ favorites_path(favorable_id: @product.id,
17
+ favorable_type: 'Spree::Product'),
18
+ method: :post,
19
+ no_text: true,
20
+ remote: true,
21
+ id: "favorable_product_#{@product.id}",
22
+ class: 'favorite_product_link fa fa-heart-o no-text with-tip' %>
23
+ <% end %>
24
+ </div>
@@ -0,0 +1,6 @@
1
+ <% if @favorite.persisted? %>
2
+ var $el = document.querySelector("#favorable_<%= @favorite.favorable_type.demodulize.underscore %>_<%= @favorite.favorable_id %>");
3
+
4
+ $el.href = '<%= favorite_path(@favorite, favorable_id: @favorite.favorable_id, favorable_type: @favorite.favorable_type) %>';
5
+ $el.dataset.method = 'delete';
6
+ <% end %>
@@ -0,0 +1,10 @@
1
+ <% if @favorite.destroyed? %>
2
+ var $el = document.querySelector("#favorable_<%= @favorite.favorable_type.demodulize.underscore %>_<%= @favorite.favorable_id %>");
3
+
4
+ if ($el) {
5
+ $el.href = '<%= favorites_path(favorable_id: @favorite.favorable_id, favorable_type: @favorite.favorable_type) %>';
6
+ $el.dataset.method = 'post';
7
+ } else {
8
+ $("#favorite_<%= @favorite.id %>").remove();
9
+ }
10
+ <% end %>
@@ -0,0 +1,52 @@
1
+ <div data-hook='account_my_favorite_products' class='account-my-favorite-products'>
2
+ <h3><%= t('spree.my_favorites') %></h3>
3
+
4
+ <% if @favorites.present? %>
5
+ <table class='favorite-products order-summary'>
6
+ <thead>
7
+ <tr>
8
+ <th class='favorite-product-image'><%= t('spree.image') %></th>
9
+ <th class='favorite-product-name'><%= t('spree.name') %></th>
10
+ <th class='favorite-product-price'><%= t('spree.price') %></th>
11
+ <th class='favorite-product-action'><%= t('spree.action')%></th>
12
+ </tr>
13
+ </thead>
14
+
15
+ <tbody>
16
+ <% @favorites.each do |favorite| %>
17
+ <tr class="favorite_item <%= cycle('even', 'odd') %>" id='favorite_<%= favorite.id %>' data-product-id='<%= favorite&.favorable&.id %>' data-variant-id='<%= favorite&.favorable&.master&.id %>'>
18
+ <td class='favorite-product-image'>
19
+ <%= link_to(render('spree/shared/image', image: favorite_image(favorite&.favorable), size: :small, itemprop: 'image'), product_path(favorite&.favorable), itemprop: 'url') %>
20
+ </td>
21
+ <td class='favorite-product-name'>
22
+ <%= favorite&.favorable.name %>
23
+ </td>
24
+ <td class='favorite-product-price'>
25
+ <% if price = favorite&.favorable.price_for(current_pricing_options) %>
26
+ <span class="price selling" itemprop="price" content="<%= price.to_d %>">
27
+ <%= price.to_html %>
28
+ </span>
29
+ <span itemprop='priceCurrency' content='<%= current_pricing_options.currency %>'></span>
30
+ <% end %>
31
+ </td>
32
+ <td class='favorite-product-actions'>
33
+ <div class='favorite-product-actions-links'>
34
+ <% if favorite.favorable.has_variants? %>
35
+ <%= link_to t('spree.go_to_product'), product_path(favorite.favorable), class: 'favorite_go_to_product_link' %>
36
+ <% else %>
37
+ <%= link_to t('spree.add_to_cart'), '#', class: 'favorite_add_to_cart_link', remote: true %>
38
+ <% end %>
39
+ <%= link_to t('spree.remove'), favorite_path(favorite), class: 'favorite_remove_link', method: :delete, remote: true %>
40
+ </div>
41
+ </tr>
42
+ <% end %>
43
+ </tbody>
44
+ </table>
45
+ <% else %>
46
+ <div class='no-objects-found'>
47
+ <p><%= t('spree.you_have_no_favorites_yet') %></p>
48
+ </div>
49
+ <% end %>
50
+
51
+ <%= paginate @favorites %>
52
+ </div
@@ -0,0 +1,25 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ spree/favorite:
5
+ favorable: Favorable
6
+ user: User
7
+ models:
8
+ spree/favorite:
9
+ one: Favorite
10
+ other: Favorites
11
+ spree:
12
+ admin:
13
+ tab:
14
+ favorites: Favorites
15
+ add_favorite: Add Favorite
16
+ favorites: Favorites
17
+ favorites_settings: Favorite Settings
18
+ favorites_per_page: Per Page
19
+ favorite_users: Favorite Users
20
+ favorite_products: Favorite Products
21
+ go_to_product: Visit product page
22
+ my_favorites: My Favorites
23
+ remove_favorite: Remove Favorite
24
+ successfully_added_favorite: Successfully added favorite.
25
+ you_have_no_favorites_yet: You have no favorites yet.
data/config/routes.rb ADDED
@@ -0,0 +1,9 @@
1
+ Spree::Core::Engine.routes.draw do
2
+ namespace :admin do
3
+ resources :favorites, only: :index do
4
+ get :users, on: :member
5
+ end
6
+ end
7
+
8
+ resources :favorites, only: [:index, :create, :destroy]
9
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateTableSpreeFavorites < SolidusSupport::Migration[5.1]
4
+ def change
5
+ create_table :spree_favorites do |t|
6
+ t.belongs_to :favorable, polymorphic: true
7
+ t.belongs_to :user, index: true
8
+ t.string :guest_token
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :spree_favorites, [:favorable_id, :favorable_type]
13
+ add_index :spree_favorites, :guest_token
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ module SolidusFavorites
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ class_option :auto_run_migrations, type: :boolean, default: false
5
+
6
+ def add_javascripts
7
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/solidus_favorites\n"
8
+ append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/solidus_favorites\n"
9
+ end
10
+
11
+ def add_stylesheets
12
+ inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/solidus_favorites\n", before: /\*\//, verbose: true
13
+ inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/solidus_favorites\n", before: /\*\//, verbose: true
14
+ end
15
+
16
+ def add_migrations
17
+ run 'bundle exec rake railties:install:migrations FROM=solidus_favorites'
18
+ end
19
+
20
+ def run_migrations
21
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
22
+ if run_migrations
23
+ run 'bundle exec rake db:migrate'
24
+ else
25
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_core'
4
+ require 'solidus_support'
5
+ require 'deface'
6
+ require 'solidus_favorites/configuration'
7
+ require 'solidus_favorites/engine'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusFavorites
4
+ class Configuration < Spree::Preferences::Configuration
5
+ preference :favorites_per_page, :integer, default: 5
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusFavorites
4
+ class Engine < Rails::Engine
5
+ isolate_namespace Spree
6
+ engine_name 'solidus_favorites'
7
+
8
+ config.autoload_paths += %W(#{config.root}/lib)
9
+
10
+ # use rspec for tests
11
+ config.generators do |g|
12
+ g.test_framework :rspec
13
+ end
14
+
15
+ initializer 'solidus_favorites.environment', before: :load_config_initializers do
16
+ SolidusFavorites::Config = SolidusFavorites::Configuration.new
17
+ end
18
+
19
+ def self.activate
20
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
21
+ Rails.configuration.cache_classes ? require(c) : load(c)
22
+ end
23
+ end
24
+
25
+ config.to_prepare(&method(:activate).to_proc)
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ FactoryBot.define do
2
+ factory :favorite, class: Spree::Favorite do
3
+ favorable { create(:product) }
4
+ user
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module SolidusFavorites
2
+ VERSION = '1.0.0'
3
+ end
metadata ADDED
@@ -0,0 +1,354 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: solidus_favorites
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Tapia
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-06-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: solidus
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3'
33
+ - !ruby/object:Gem::Dependency
34
+ name: solidus_auth_devise
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '3'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '2.0'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '3'
53
+ - !ruby/object:Gem::Dependency
54
+ name: solidus_support
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: deface
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.0'
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '1.0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: capybara
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: capybara-screenshot
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ - !ruby/object:Gem::Dependency
110
+ name: coffee-rails
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: database_cleaner
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ - !ruby/object:Gem::Dependency
138
+ name: factory_bot
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ - !ruby/object:Gem::Dependency
152
+ name: ffaker
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ - !ruby/object:Gem::Dependency
166
+ name: poltergeist
167
+ requirement: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ type: :development
173
+ prerelease: false
174
+ version_requirements: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ - !ruby/object:Gem::Dependency
180
+ name: puma
181
+ requirement: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ type: :development
187
+ prerelease: false
188
+ version_requirements: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ - !ruby/object:Gem::Dependency
194
+ name: rspec-rails
195
+ requirement: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ type: :development
201
+ prerelease: false
202
+ version_requirements: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
207
+ - !ruby/object:Gem::Dependency
208
+ name: rubocop
209
+ requirement: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - ">="
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ type: :development
215
+ prerelease: false
216
+ version_requirements: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - ">="
219
+ - !ruby/object:Gem::Version
220
+ version: '0'
221
+ - !ruby/object:Gem::Dependency
222
+ name: rubocop-rspec
223
+ requirement: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - ">="
226
+ - !ruby/object:Gem::Version
227
+ version: '0'
228
+ type: :development
229
+ prerelease: false
230
+ version_requirements: !ruby/object:Gem::Requirement
231
+ requirements:
232
+ - - ">="
233
+ - !ruby/object:Gem::Version
234
+ version: '0'
235
+ - !ruby/object:Gem::Dependency
236
+ name: sass-rails
237
+ requirement: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - ">="
240
+ - !ruby/object:Gem::Version
241
+ version: '0'
242
+ type: :development
243
+ prerelease: false
244
+ version_requirements: !ruby/object:Gem::Requirement
245
+ requirements:
246
+ - - ">="
247
+ - !ruby/object:Gem::Version
248
+ version: '0'
249
+ - !ruby/object:Gem::Dependency
250
+ name: shoulda-matchers
251
+ requirement: !ruby/object:Gem::Requirement
252
+ requirements:
253
+ - - ">="
254
+ - !ruby/object:Gem::Version
255
+ version: '0'
256
+ type: :development
257
+ prerelease: false
258
+ version_requirements: !ruby/object:Gem::Requirement
259
+ requirements:
260
+ - - ">="
261
+ - !ruby/object:Gem::Version
262
+ version: '0'
263
+ - !ruby/object:Gem::Dependency
264
+ name: simplecov
265
+ requirement: !ruby/object:Gem::Requirement
266
+ requirements:
267
+ - - ">="
268
+ - !ruby/object:Gem::Version
269
+ version: '0'
270
+ type: :development
271
+ prerelease: false
272
+ version_requirements: !ruby/object:Gem::Requirement
273
+ requirements:
274
+ - - ">="
275
+ - !ruby/object:Gem::Version
276
+ version: '0'
277
+ - !ruby/object:Gem::Dependency
278
+ name: sqlite3
279
+ requirement: !ruby/object:Gem::Requirement
280
+ requirements:
281
+ - - ">="
282
+ - !ruby/object:Gem::Version
283
+ version: '0'
284
+ type: :development
285
+ prerelease: false
286
+ version_requirements: !ruby/object:Gem::Requirement
287
+ requirements:
288
+ - - ">="
289
+ - !ruby/object:Gem::Version
290
+ version: '0'
291
+ description: Users can mark favorite products or anything else you want to allow
292
+ email: jonathan.tapia@magmalabs.io
293
+ executables: []
294
+ extensions: []
295
+ extra_rdoc_files: []
296
+ files:
297
+ - LICENSE
298
+ - README.md
299
+ - Rakefile
300
+ - app/assets/javascripts/spree/backend/solidus_favorites.js
301
+ - app/assets/javascripts/spree/frontend/solidus_favorites.js
302
+ - app/assets/stylesheets/spree/backend/solidus_favorites.css
303
+ - app/assets/stylesheets/spree/frontend/solidus_favorites.css
304
+ - app/controllers/spree/admin/favorites_controller.rb
305
+ - app/controllers/spree/favorites_controller.rb
306
+ - app/helpers/solidus_favorites/store_helper_decorator.rb
307
+ - app/helpers/spree/store_helper_decorator.rb
308
+ - app/models/solidus_favorites/product_decorator.rb
309
+ - app/models/solidus_favorites/user_decorator.rb
310
+ - app/models/spree/favorite.rb
311
+ - app/models/spree/product_decorator.rb
312
+ - app/models/spree/user_decorator.rb
313
+ - app/overrides/add_favorite_link_to_product_show.rb
314
+ - app/overrides/add_favorite_to_admin_product_sidebar.rb
315
+ - app/views/spree/admin/favorites/index.html.erb
316
+ - app/views/spree/admin/favorites/users.html.erb
317
+ - app/views/spree/favorites/_product_link.html.erb
318
+ - app/views/spree/favorites/create.js.erb
319
+ - app/views/spree/favorites/destroy.js.erb
320
+ - app/views/spree/favorites/index.html.erb
321
+ - config/locales/en.yml
322
+ - config/routes.rb
323
+ - db/migrate/20190624204433_create_table_spree_favorites.rb
324
+ - lib/generators/solidus_favorites/install/install_generator.rb
325
+ - lib/solidus_favorites.rb
326
+ - lib/solidus_favorites/configuration.rb
327
+ - lib/solidus_favorites/engine.rb
328
+ - lib/solidus_favorites/factories.rb
329
+ - lib/solidus_favorites/version.rb
330
+ homepage: http://github.com/jtapia/solidus_favorites
331
+ licenses:
332
+ - BSD-3-Clause
333
+ metadata: {}
334
+ post_install_message:
335
+ rdoc_options: []
336
+ require_paths:
337
+ - lib
338
+ required_ruby_version: !ruby/object:Gem::Requirement
339
+ requirements:
340
+ - - ">="
341
+ - !ruby/object:Gem::Version
342
+ version: '0'
343
+ required_rubygems_version: !ruby/object:Gem::Requirement
344
+ requirements:
345
+ - - ">="
346
+ - !ruby/object:Gem::Version
347
+ version: '0'
348
+ requirements: []
349
+ rubyforge_project:
350
+ rubygems_version: 2.7.6
351
+ signing_key:
352
+ specification_version: 4
353
+ summary: Users can mark favorite products or anything else you want to allow
354
+ test_files: []