solidus_favorites 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []