spree_active_sale 1.0.6 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/README.md +198 -56
  2. data/app/controllers/spree/admin/active_sale_events_controller.rb +52 -0
  3. data/app/controllers/spree/admin/active_sales_controller.rb +25 -0
  4. data/app/controllers/spree/admin/sale_images_controller.rb +33 -0
  5. data/app/controllers/spree/checkout_controller_decorator.rb +6 -0
  6. data/app/controllers/spree/home_controller_decorator.rb +10 -0
  7. data/app/controllers/spree/orders_controller_decorator.rb +6 -0
  8. data/app/controllers/{products_controller_decorator.rb → spree/products_controller_decorator.rb} +5 -1
  9. data/app/controllers/spree/store_controller_decorator.rb +11 -0
  10. data/app/controllers/{taxons_controller_decorator.rb → spree/taxons_controller_decorator.rb} +1 -1
  11. data/app/helpers/spree/active_sale_events_helper.rb +46 -0
  12. data/app/helpers/spree/active_sales_helper.rb +4 -0
  13. data/app/models/spree/active_sale.rb +12 -6
  14. data/app/models/spree/active_sale_configuration.rb +13 -0
  15. data/app/models/spree/active_sale_event.rb +58 -0
  16. data/app/models/spree/line_item_decorator.rb +6 -0
  17. data/app/models/spree/order_decorator.rb +6 -0
  18. data/app/models/spree/product_decorator.rb +14 -3
  19. data/app/models/spree/sale_event.rb +86 -0
  20. data/app/models/spree/sale_image.rb +51 -0
  21. data/app/models/spree/taxon_decorator.rb +11 -2
  22. data/app/models/spree/variant_decorator.rb +2 -2
  23. data/app/overrides/admin_active_sales_tab.rb +6 -0
  24. data/app/views/spree/admin/active_sale_events/_form.html.erb +136 -0
  25. data/app/views/spree/admin/active_sale_events/edit.html.erb +17 -0
  26. data/app/views/spree/admin/active_sale_events/index.html.erb +12 -0
  27. data/app/views/spree/admin/active_sale_events/new.html.erb +15 -0
  28. data/app/views/spree/admin/active_sales/_form.html.erb +35 -0
  29. data/app/views/spree/admin/active_sales/edit.html.erb +17 -0
  30. data/app/views/spree/admin/active_sales/index.html.erb +57 -0
  31. data/app/views/spree/admin/active_sales/new.html.erb +15 -0
  32. data/app/views/spree/admin/sale_images/_form.html.erb +18 -0
  33. data/app/views/spree/admin/sale_images/edit.html.erb +24 -0
  34. data/app/views/spree/admin/sale_images/new.html.erb +19 -0
  35. data/app/views/spree/admin/shared/_list_events.html.erb +32 -0
  36. data/app/views/spree/admin/shared/_sale_images.html.erb +41 -0
  37. data/app/views/spree/admin/shared/_translations.html.erb +57 -0
  38. data/app/views/spree/home/index.html.erb +4 -0
  39. data/app/views/spree/shared/_sale_events.html.erb +28 -0
  40. data/config/locales/active_sales_en.yml +74 -0
  41. data/config/locales/active_sales_es.yml +74 -0
  42. data/config/routes.rb +12 -0
  43. data/db/migrate/20130404062459_create_sale_events.rb +70 -0
  44. data/db/migrate/20130411091742_add_discount_to_sale_events.rb +5 -0
  45. data/lib/generators/spree_active_sale/assets/assets_generator.rb +37 -0
  46. data/lib/generators/spree_active_sale/templates/README.md +3 -0
  47. data/lib/spree_active_sale/engine.rb +7 -0
  48. data/lib/tasks/spree_active_sale.rake +14 -6
  49. metadata +46 -11
  50. data/app/assets/javascripts/admin/spree_active_sale.js +0 -1
  51. data/app/assets/javascripts/store/spree_active_sale.js +0 -1
  52. data/app/assets/stylesheets/admin/spree_active_sale.css +0 -3
  53. data/app/assets/stylesheets/store/spree_active_sale.css +0 -3
  54. data/app/models/spree/active_sale/event.rb +0 -57
  55. data/config/locales/en.yml +0 -12
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ StoreController.class_eval do
3
+
4
+ # Check if all products in cart are still live.
5
+ # Also, delete line item if any of the products from cart is expired.
6
+ def check_active_products_in_order
7
+ current_order.delete_inactive_items unless current_order.nil?
8
+ end
9
+
10
+ end
11
+ end
@@ -5,7 +5,7 @@ module Spree
5
5
  @taxon = Spree::Taxon.find_by_permalink!(params[:id])
6
6
  return unless @taxon
7
7
 
8
- if Spree::ActiveSale::Event.is_live? @taxon
8
+ if @taxon.live?
9
9
  @searcher = Spree::Config.searcher_class.new(params.merge(:taxon => @taxon.id))
10
10
  @products = @searcher.retrieve_products
11
11
 
@@ -0,0 +1,46 @@
1
+ module Spree
2
+ module ActiveSaleEventsHelper
3
+
4
+ def sale_event_timer(event = nil, layout = nil)
5
+ return I18n.t('spree.active_sale.event.can_not_be_nil') if (event == nil) || (event.class.name != "Spree::ActiveSaleEvent")
6
+ layout ||= '{dn} DAYS {hnn}{sep}{mnn}{sep}{snn}'
7
+ content_tag(:span, I18n.t('spree.active_sale.event.ending_message'), :class => 'sale_event_message') + " " + content_tag(:span, event.end_date.strftime('%Y-%m-%dT%H:%M:%S'), "data-timer" => event.end_date.strftime('%Y-%m-%dT%H:%M:%S'), "data-layout" => layout, :class => 'sale_event_message')
8
+ end
9
+
10
+ def method_missing(method_name, *args, &block)
11
+ if image_style = image_style_from_method_name_for_sale(method_name)
12
+ define_sale_image_method(image_style)
13
+ self.send(method_name, *args)
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ def sale_image_available?(sale_event)
20
+ !sale_event.sale_images.empty? || sale_event.eventable_image_available?
21
+ end
22
+
23
+ private
24
+
25
+ # Returns style of image or nil
26
+ def image_style_from_method_name_for_sale(method_name)
27
+ if style = method_name.to_s.sub(/_event_image$/, '')
28
+ possible_styles = Spree::SaleImage.attachment_definitions[:attachment][:styles]
29
+ style if style.in? possible_styles.with_indifferent_access
30
+ end
31
+ end
32
+
33
+ def define_sale_image_method(style)
34
+ self.class.send :define_method, "#{style}_event_image" do |sale_event, *options|
35
+ options = options.first || {}
36
+ if sale_event.sale_images.empty?
37
+ image_tag "noimage/#{style}.png", options
38
+ else
39
+ image = sale_event.sale_images.first
40
+ options.reverse_merge! :alt => image.alt.blank? ? sale_event.name : image.alt
41
+ image_tag image.attachment.url(style), options
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,4 @@
1
+ module Spree
2
+ module ActiveSalesHelper
3
+ end
4
+ end
@@ -3,14 +3,20 @@
3
3
  # For example: 'January 2013' sale, which can have many sale events in it.
4
4
  #
5
5
  module Spree
6
- class ActiveSale < ActiveRecord::Base
7
- has_many :events, :class_name => "Spree::ActiveSale::Event"
8
- belongs_to :taxon, :class_name => "Spree::Taxon"
9
-
10
- attr_accessible :name
6
+ class ActiveSale < Spree::SaleEvent
7
+ acts_as_nested_set :dependent => :destroy
8
+ has_many :active_sale_events
11
9
 
12
10
  validates :name, :presence => true
13
11
 
14
- accepts_nested_attributes_for :events, :allow_destroy => true, :reject_if => lambda { |attrs| attrs.all? { |k, v| v.blank? } }
12
+ accepts_nested_attributes_for :active_sale_events, :allow_destroy => true, :reject_if => lambda { |attrs| attrs.all? { |k, v| v.blank? } }
13
+
14
+ def self.config(&block)
15
+ yield(Spree::ActiveSaleConfig)
16
+ end
17
+
18
+ def children_and_active_sale_events
19
+ ((self.descendants.map{ |child_sale| child_sale.children_and_active_sale_events } << self) + self.active_sale_events).flatten.reject(&:blank?)
20
+ end
15
21
  end
16
22
  end
@@ -0,0 +1,13 @@
1
+ # More on Spree's preference configuration - http://guides.spreecommerce.com/preferences.html#site_wide_preferences
2
+ module Spree
3
+ class ActiveSaleConfiguration < Preferences::Configuration
4
+ preference :paginate_sale_events_for_admin?, :boolean, :default => false
5
+ preference :paginate_sales_for_admin?, :boolean, :default => false
6
+ preference :paginate_sale_events_for_user?, :boolean, :default => false
7
+ preference :paginate_sales_for_user?, :boolean, :default => false
8
+ preference :admin_active_sales_per_page, :integer, :default => 25
9
+ preference :active_sales_per_page, :integer, :default => 25
10
+ preference :admin_active_sale_events_per_page, :integer, :default => 25
11
+ preference :active_sale_events_per_page, :integer, :default => 25
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ # EVENTS
2
+ # Events represent an entity for active sale in a flash sale/ daily deal.
3
+ # There can be many events for one sale.
4
+ #
5
+ module Spree
6
+ class ActiveSaleEvent < Spree::SaleEvent
7
+ before_validation :update_permalink
8
+ after_save :update_parent_active_sales
9
+
10
+ has_many :sale_images, :as => :viewable, :dependent => :destroy, :order => 'position ASC'
11
+ belongs_to :eventable, :polymorphic => true
12
+ belongs_to :active_sale
13
+
14
+ attr_accessible :description, :end_date, :eventable_id, :eventable_type, :is_active, :is_hidden, :is_permanent, :name, :permalink, :active_sale_id, :start_date, :eventable_name, :discount
15
+
16
+ validates :name, :permalink, :eventable_id, :start_date, :end_date, :active_sale_id, :presence => true
17
+ validates :eventable_type, :presence => true, :uniqueness => { :scope => :eventable_id, :message => I18n.t('spree.active_sale.event.validation.errors.live_event') }, :if => :live?
18
+ validate :validate_start_and_end_date
19
+
20
+ # Spree::ActiveSaleEvent.is_live? method
21
+ # should only/ always represents live and active events and not just live events.
22
+ def is_live? object
23
+ object_class_name = object.class.name
24
+ return object.live_and_active? if object_class_name == self.name
25
+ %w(Spree::Product Spree::Variant Spree::Taxon).include?(object_class_name) ? object.live? : false
26
+ end
27
+
28
+ def update_permalink
29
+ prefix = {"Spree::Taxon" => "t", "Spree::Product" => "products"}
30
+ self.permalink = [prefix[self.eventable_type], self.eventable.permalink].join("/") unless self.eventable.nil?
31
+ end
32
+
33
+ # This callback basically makes sure that parents for an event lives longer.
34
+ # Or at least parents live for the time when event is live.
35
+ def update_parent_active_sales
36
+ active_sale_events = self.active_sale.children_and_active_sale_events
37
+ parents = self.active_sale.self_and_ancestors.flatten
38
+ oldest_start_date = active_sale_events.map(&:start_date).reject(&:blank?).sort.first
39
+ latest_end_date = active_sale_events.map(&:end_date).reject(&:blank?).sort.last
40
+ parents.each{ |parent|
41
+ parent.update_attributes(:start_date => oldest_start_date) if parent.start_date.nil? ? true : (parent.start_date > oldest_start_date)
42
+ parent.update_attributes(:end_date => latest_end_date) if parent.end_date.nil? ? true : (parent.end_date < latest_end_date)
43
+ }
44
+ end
45
+
46
+ def eventable_name
47
+ eventable.try(:name)
48
+ end
49
+
50
+ def eventable_name=(name)
51
+ self.eventable = self.eventable_type.constantize.find_by_name(name) if name.present?
52
+ end
53
+
54
+ def eventable_image_available?
55
+ !!eventable.try(:image_available?)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,6 @@
1
+ Spree::LineItem.class_eval do
2
+
3
+ def live?
4
+ self.product.live?
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ Spree::Order.class_eval do
2
+
3
+ def delete_inactive_items
4
+ self.line_items.each{ |line_item| line_item.destroy unless line_item.live? }
5
+ end
6
+ end
@@ -1,11 +1,22 @@
1
1
  Spree::Product.class_eval do
2
- has_many :active_sale_events, :as => :eventable, :class_name => "Spree::ActiveSale::Event"
2
+ has_many :active_sale_events, :as => :eventable
3
3
 
4
+ # Find live and active taxons for a product.
4
5
  def find_live_taxons
5
- Spree::ActiveSale::Event.live_active.where(:eventable_type => "Spree::Taxon", :eventable_id => self.taxons.map(&:id))
6
+ Spree::ActiveSaleEvent.live_active.where(:eventable_type => "Spree::Taxon", :eventable_id => self.taxons.map(&:id))
6
7
  end
7
8
 
9
+ # if there is at least one active sale event which is live and active.
8
10
  def live?
9
- !self.active_sale_events.detect{ |event| (event.live_and_active?) }.nil? || !self.find_live_taxons.blank?
11
+ !self.active_sale_events.detect{ |event| event.live_and_active? }.nil? || !self.find_live_taxons.blank?
12
+ end
13
+
14
+ # Check if image is available for this.
15
+ def image_available?
16
+ !images.blank?
17
+ end
18
+
19
+ def image
20
+ images.first
10
21
  end
11
22
  end
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+ # Sale Event
3
+ # Sale Event represents active sale and active sale event by using STI.
4
+ # There can be many events for one sale.
5
+ #
6
+ module Spree
7
+ class SaleEvent < ActiveRecord::Base
8
+ attr_accessible :description, :end_date, :eventable_id, :eventable_type, :is_active, :is_hidden, :is_permanent, :name, :permalink, :active_sale_id, :start_date, :eventable_name, :type, :parent_id, :position
9
+
10
+ scope :live, lambda { where("(start_date <= :start_date AND end_date >= :end_date) OR is_permanent = :is_permanent", { :start_date => zone_time, :end_date => zone_time, :is_permanent => true }) }
11
+ scope :active, lambda { |*args| where(:is_active => valid_argument(args)) }
12
+ scope :hidden, lambda { |*args| where(:is_hidden => valid_argument(args)) }
13
+ scope :live_active, lambda { |*args| self.live.active(valid_argument(args)) }
14
+ scope :live_active_and_hidden, lambda { |*args|
15
+ args = [{}] if [nil, true, false].include? args.first
16
+ self.live.active(valid_argument([args.first[:active]])).hidden(valid_argument([args.first[:hidden]]))
17
+ }
18
+ scope :upcoming_events, lambda { where("start_date > :start_date", { :start_date => zone_time }) }
19
+ scope :past_events, lambda { where("end_date < :end_date", { :end_date => zone_time }) }
20
+ scope :starting_today, lambda { where(:start_date => zone_time..zone_time.end_of_day) }
21
+ scope :ending_today, lambda { where(:end_date => zone_time..zone_time.end_of_day) }
22
+
23
+
24
+ def validate_start_and_end_date
25
+ errors.add(:start_date, I18n.t('spree.active_sale.event.validation.errors.invalid_dates')) if invalid_dates?
26
+ end
27
+
28
+ def live?(moment=nil)
29
+ moment ||= object_zone_time
30
+ (self.start_date <= moment and self.end_date >= moment) or self.is_permanent? if start_and_dates_available?
31
+ end
32
+
33
+ def upcoming?
34
+ current_time = object_zone_time
35
+ (self.start_date >= current_time and self.end_date > self.start_date) if start_and_dates_available?
36
+ end
37
+
38
+ def past?
39
+ current_time = object_zone_time
40
+ (self.start_date < current_time and self.end_date > self.start_date and self.end_date < current_time) if start_and_dates_available?
41
+ end
42
+
43
+ def live_and_active?(moment=nil)
44
+ self.live?(moment) and self.is_active?
45
+ end
46
+
47
+ def start_and_dates_available?
48
+ self.start_date and self.end_date
49
+ end
50
+
51
+ def invalid_dates?
52
+ self.start_and_dates_available? and (self.start_date >= self.end_date)
53
+ end
54
+
55
+ class << self
56
+ # Class methods
57
+ def paginate(objects_per_page, options = {})
58
+ options = prepare_pagination(objects_per_page, options)
59
+ self.page(options[:page]).per(options[:per_page])
60
+ end
61
+
62
+ private
63
+ def valid_argument args
64
+ (args.first == nil || args.first == true)
65
+ end
66
+
67
+ def prepare_pagination(objects_per_page, options)
68
+ per_page = options[:per_page].to_i
69
+ options[:per_page] = per_page > 0 ? per_page : Spree::ActiveSaleConfig[objects_per_page]
70
+ page = options[:page].to_i
71
+ options[:page] = page > 0 ? page : 1
72
+ options
73
+ end
74
+
75
+ def zone_time
76
+ Time.zone.now
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def object_zone_time
83
+ Time.zone.now
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,51 @@
1
+ module Spree
2
+ class SaleImage < Asset
3
+ validates_attachment_presence :attachment
4
+ validate :no_attachment_errors
5
+
6
+ attr_accessible :alt, :attachment, :position, :viewable_type, :viewable_id
7
+
8
+ has_attached_file :attachment,
9
+ :styles => { :mini => '48x48>', :small => '100x100>', :sale => '240x240>', :large => '600x600>' },
10
+ :default_style => :sale,
11
+ :url => '/spree/sales/:id/:style/:basename.:extension',
12
+ :path => ':rails_root/public/spree/sales/:id/:style/:basename.:extension',
13
+ :convert_options => { :all => '-strip' }
14
+ # save the w,h of the original image (from which others can be calculated)
15
+ # we need to look at the write-queue for images which have not been saved yet
16
+ after_post_process :find_dimensions
17
+
18
+ include Spree::Core::S3Support
19
+ supports_s3 :attachment
20
+
21
+ Spree::Image.attachment_definitions[:attachment][:styles] = ActiveSupport::JSON.decode(Spree::Config[:attachment_styles])
22
+ Spree::Image.attachment_definitions[:attachment][:path] = Spree::Config[:attachment_path]
23
+ Spree::Image.attachment_definitions[:attachment][:url] = Spree::Config[:attachment_url]
24
+ Spree::Image.attachment_definitions[:attachment][:default_url] = Spree::Config[:attachment_default_url]
25
+ Spree::Image.attachment_definitions[:attachment][:default_style] = Spree::Config[:attachment_default_style]
26
+
27
+ #used by admin sales autocomplete
28
+ def mini_url
29
+ attachment.url(:mini, false)
30
+ end
31
+
32
+ def find_dimensions
33
+ temporary = attachment.queued_for_write[:original]
34
+ filename = temporary.path unless temporary.nil?
35
+ filename = attachment.path if filename.blank?
36
+ geometry = Paperclip::Geometry.from_file(filename)
37
+ self.attachment_width = geometry.width
38
+ self.attachment_height = geometry.height
39
+ end
40
+
41
+ # if there are errors from the plugin, then add a more meaningful message
42
+ def no_attachment_errors
43
+ unless attachment.errors.empty?
44
+ # uncomment this to get rid of the less-than-useful interrim messages
45
+ # errors.clear
46
+ errors.add :attachment, "Paperclip returned errors for file '#{attachment_file_name}' - check ImageMagick installation or image source file."
47
+ false
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,7 +1,16 @@
1
1
  Spree::Taxon.class_eval do
2
- has_many :active_sale_events, :as => :eventable, :class_name => "Spree::ActiveSale::Event"
2
+ has_many :active_sale_events, :as => :eventable
3
3
 
4
+ # if there is at least one active sale event which is live and active.
4
5
  def live?
5
- !self.active_sale_events.detect{ |event| (event.live_and_active?) }.nil?
6
+ !self.active_sale_events.detect{ |event| event.live_and_active? }.nil?
7
+ end
8
+
9
+ def image_available?
10
+ icon?
11
+ end
12
+
13
+ def image
14
+ icon
6
15
  end
7
16
  end
@@ -1,7 +1,7 @@
1
1
  Spree::Variant.class_eval do
2
- has_many :active_sale_events, :as => :eventable, :class_name => "Spree::ActiveSale::Event"
2
+ has_many :active_sale_events, :as => :eventable
3
3
 
4
4
  def live?
5
- !self.active_sale_events.detect{ |event| (event.live_and_active?) }.nil? || self.product.live?
5
+ !self.active_sale_events.detect{ |event| event.live_and_active? }.nil? || self.product.live?
6
6
  end
7
7
  end
@@ -0,0 +1,6 @@
1
+ Deface::Override.new(
2
+ :name => "active_sale_tab",
3
+ :virtual_path => "spree/layouts/admin",
4
+ :insert_bottom => "[data-hook='admin_tabs'], #admin_tabs[data-hook]",
5
+ :text => "<%= tab :active_sales, :icon => 'icon-th-list' %>"
6
+ )
@@ -0,0 +1,136 @@
1
+ <% if @active_sale_event.try(:errors).present? %>
2
+ <%= render :partial => 'spree/shared/error_messages', :locals => { :target => @active_sale_event } %>
3
+ <% end %>
4
+
5
+ <%= form_for [:admin, @active_sale, @active_sale_event], :html => { :id => 'admin_active_sale_event', :multipart => true } do |f| %>
6
+ <fieldset data-hook="admin_active_sale_event_form_fields">
7
+
8
+ <div class="clearfix">
9
+ <div class="left eight columns alpha" data-hook="admin_active_sale_event_form_left">
10
+ <%= f.field_container :name do %>
11
+ <%= f.label :name, raw(t('spree.active_sale.event.active_record.name') + content_tag(:span, ' *', :class => 'required')) %>
12
+ <%= f.text_field :name, :class => 'fullwidth' %>
13
+ <%= f.error_message_on :name %>
14
+ <% end %>
15
+ <%= f.field_container :description do %>
16
+ <%= f.label :description, t('spree.active_sale.event.active_record.description') %>
17
+ <%= f.text_area :description, {:cols => 60, :rows => 10, :class => 'fullwidth'} %>
18
+ <%= f.error_message_on :description %>
19
+ <% end %>
20
+ </div>
21
+
22
+ <div class="right eight columns omega" data-hook="admin_active_sale_event_form_right">
23
+ <div class="row">
24
+ <div class="alpha four columns">
25
+
26
+ <%= f.field_container :start_date do %>
27
+ <%= f.label :start_date, t('spree.active_sale.event.active_record.start_date') %>
28
+ <%= f.error_message_on :start_date %>
29
+ <% if @active_sale_event.start_date? %>
30
+ <% start_date = l(@active_sale_event.start_date, :format => t('spree.active_sale.event.datetimepicker.format')) %>
31
+ <% else %>
32
+ <% start_date = l(Time.zone.now, :format => t('spree.active_sale.event.datetimepicker.format')) %>
33
+ <% end %>
34
+ <%= f.text_field :start_date, :value => start_date, :class => 'timepicker' %>
35
+ <% end %>
36
+ </div>
37
+
38
+ <div class="omega four columns">
39
+ <%= f.field_container :end_date do %>
40
+ <%= f.label :end_date, t('spree.active_sale.event.active_record.end_date') %>
41
+ <%= f.error_message_on :end_date %>
42
+ <% end_date = l((@active_sale_event.end_date? ? @active_sale_event.end_date : Time.zone.now+1.day), :format => t('spree.active_sale.event.datetimepicker.format')) %>
43
+ <%= f.text_field :end_date, :value => end_date, :class => 'timepicker' %>
44
+ <% end %>
45
+ </div>
46
+ </div>
47
+ <div class="row">
48
+ <div class="alpha four columns">
49
+ <%= f.label :eventable, t('spree.active_sale.event.active_record.eventable_type', :default => :evenetable) %>
50
+ <p class='hint'><%= t('spree.active_sale.event.eventable_hint') %></p>
51
+ <%= f.select :eventable_type, [["Product","Spree::Product"], ["Taxon","Spree::Taxon"]], { :include_blank => false }, { :class => 'select2' } %>
52
+ <%= f.error_message_on :eventable %>
53
+ </div>
54
+
55
+ <div class="omega four columns">
56
+ <%= f.label :eventable_name, t('spree.active_sale.event.active_record.eventable_name', :default => :eventable_name) %>
57
+ <p class='hint'><%= t('spree.active_sale.event.eventable_name_hint') %></p>
58
+ <%= f.error_message_on :eventable_name %>
59
+ <%= f.text_field :eventable_name, :class => 'autocomplete', :data => { :auto_complete_url => eventables_admin_active_sale_active_sale_events_url(:active_sale_id => @active_sale.id) } %>
60
+ </div>
61
+ </div>
62
+ <div class="row">
63
+ <div class="alpha four columns clearfix">
64
+ <%= f.field_container :is_active, :class => ['checkbox'] do %>
65
+ <label>
66
+ <%= f.check_box :is_active %>
67
+ <%= t('spree.active_sale.event.active_record.is_active') %>
68
+ <%= f.error_message_on :is_active %>
69
+ </label>
70
+ <% end %>
71
+
72
+ <%= f.field_container :is_hidden, :class => ['checkbox'] do %>
73
+ <label>
74
+ <%= f.check_box :is_hidden %>
75
+ <%= t('spree.active_sale.event.active_record.is_hidden') %>
76
+ <%= f.error_message_on :is_hidden %>
77
+ </label>
78
+ <% end %>
79
+
80
+ <%= f.field_container :is_permanent, :class => ['checkbox'] do %>
81
+ <label>
82
+ <%= f.check_box :is_permanent %>
83
+ <%= t('spree.active_sale.event.active_record.is_permanent') %>
84
+ <%= f.error_message_on :is_permanent %>
85
+ </label>
86
+ <% end %>
87
+ </div>
88
+
89
+ <div class="omega four columns">
90
+ <%= f.field_container :discount do %>
91
+ <%= f.label :discount, t('spree.active_sale.event.active_record.discount', :default => :discount) %>
92
+ <p class='hint'><%= t('spree.active_sale.event.discount_hint') %></p>
93
+ <%= f.error_message_on :discount %>
94
+ <%= f.text_field :discount, :size => 2, :maxlength => 2 %>
95
+ <% end %>
96
+ </div>
97
+ </div>
98
+
99
+ </div>
100
+ </div>
101
+
102
+ <% unless @active_sale_event.new_record? %>
103
+ <ul class="inline-menu">
104
+ <li><%= link_to_with_icon('icon-plus', t(:new_image), new_admin_active_sale_active_sale_event_sale_image_url(@active_sale, @active_sale_event), :id => 'new_sale_image_link', :class => 'button') %></li>
105
+ </ul>
106
+ <br/>
107
+ <%= render 'spree/admin/shared/sale_images' %>
108
+ <% end %>
109
+
110
+
111
+ <p class="form-buttons" data-hook="admin_active_sale_form_buttons">
112
+ <% if @active_sale_event.new_record? %>
113
+ <%= render :partial => 'spree/admin/shared/new_resource_links' %>
114
+ <% else %>
115
+ <%= render :partial => 'spree/admin/shared/edit_resource_links' %>
116
+ <% end %>
117
+ </p>
118
+ </fieldset>
119
+ <% end %>
120
+
121
+ <%= javascript_tag do -%>
122
+ var properties = "<%= raw(@properties.to_json) %>";
123
+
124
+ $("#admin_active_sale_event input.autocomplete").live("keydown", function(){
125
+ var eventable_type = $('#active_sale_event_eventable_type :selected').val();
126
+ var eventable_name = $('#active_sale_event_eventable_name').val();
127
+ var url = $(this).data('auto-complete-url')
128
+ already_auto_completed = $(this).is('ac_input');
129
+ if (!already_auto_completed) {
130
+ $(this).autocomplete({
131
+ source: url+"?eventable_type="+eventable_type+"&name="+eventable_name
132
+ });
133
+ $(this).focus();
134
+ }
135
+ });
136
+ <% end -%>