Merge branch 'develop' into feature/vst

This commit is contained in:
Seth Call 2015-11-13 11:06:07 -06:00
commit 021db62539
92 changed files with 2008 additions and 1793 deletions

View File

@ -0,0 +1,37 @@
ActiveAdmin.register JamRuby::AffiliateQuarterlyPayment, :as => 'Affiliate Quarterly Payments' do
menu :label => 'Quarterly Reports', :parent => 'Affiliates'
config.sort_order = 'due_amount_in_cents DESC'
config.batch_actions = false
config.clear_action_items!
config.filters = true
config.per_page = 50
config.paginate = true
filter :affiliate_partner
filter :year
filter :quarter
filter :closed
filter :paid
form :partial => 'form'
index do
# default_actions # use this for all view/edit/delete links
column 'Year' do |oo| oo.year end
column 'Quarter' do |oo| oo.quarter end
column 'Partner' do |oo| link_to(oo.affiliate_partner.display_name, oo.affiliate_partner.admin_url, {:title => oo.affiliate_partner.display_name}) end
column "Due (\u00A2)" do |oo| oo.due_amount_in_cents end
column 'JamTracks Sold' do |oo| oo.jamtracks_sold end
column 'Paid' do |oo| oo.paid end
column 'Closed' do |oo| oo.paid end
end
controller do
end
end

View File

@ -0,0 +1,41 @@
ActiveAdmin.register_page "Giftcarduploads" do
menu :label => 'Gift Cards Upload', :parent => 'JamTracks'
page_action :upload_giftcards, :method => :post do
GiftCard.transaction do
puts params
file = params[:jam_ruby_gift_card][:csv]
array_of_arrays = CSV.read(file.tempfile.path)
array_of_arrays.each do |row|
if row.length != 1
raise "UKNONWN CSV FORMAT! Must be 1 column"
end
code = row[0]
gift_card = GiftCard.new
gift_card.code = code
gift_card.card_type = params[:jam_ruby_gift_card][:card_type]
gift_card.origin = file .original_filename
gift_card.save!
end
redirect_to admin_giftcarduploads_path, :notice => "Created #{array_of_arrays.length} gift cards!"
end
end
content do
semantic_form_for GiftCard.new, :url => admin_giftcarduploads_upload_giftcards_path, :builder => ActiveAdmin::FormBuilder do |f|
f.inputs "Upload Gift Cards" do
f.input :csv, as: :file, required: true, :label => "A single column CSV that contains ONE type of gift card (5 JamTrack, 10 JamTrack, etc)"
f.input :card_type, required:true, as: :select, :collection => JamRuby::GiftCard::CARD_TYPES
end
f.actions
end
end
end

View File

@ -0,0 +1,24 @@
ActiveAdmin.register JamRuby::GiftCard, :as => 'GiftCards' do
menu :label => 'Gift Cards', :parent => 'JamTracks'
config.batch_actions = false
config.filters = true
config.per_page = 50
scope("Redeemed Most Recently", default: true) { |scope| scope.where('user_id IS NOT NULL').order('updated_at DESC') }
scope("Available") { |scope| scope.where('user_id is NULL') }
filter :card_type
filter :origin
filter :code
index do
column 'User' do |oo| oo.user ? link_to(oo.user.email, oo.user.admin_url, {:title => oo.user.email}) : '' end
column 'Code' do |oo| oo.code end
column 'Card Type' do |oo| oo.card_type end
column 'Origin' do |oo| oo.origin end
column 'Created' do |oo| oo.created_at end
end
end

View File

@ -0,0 +1,40 @@
ActiveAdmin.register JamRuby::SaleLineItem, :as => 'Sale Line Items' do
menu :label => 'Line Items', :parent => 'Purchases'
config.sort_order = 'created_at DESC'
config.batch_actions = false
config.clear_action_items!
config.filters = true
config.per_page = 50
config.paginate = true
filter :affiliate_referral_id
filter :free
form :partial => 'form'
scope("Non Free", default: true) { |scope| scope.where(free: false).order('created_at desc') }
scope("Free") { |scope| scope.where(free: true).order('created_at desc') }
index do
# default_actions # use this for all view/edit/delete links
column 'Product' do |oo| oo.product end
column "Partner" do |oo|
link_to("#{oo.affiliate_referral.display_name} #{oo.affiliate_referral_fee_in_cents ? "#{oo.affiliate_referral_fee_in_cents}\u00A2" : ''}", oo.affiliate_referral.admin_url, {:title => oo.affiliate_referral.display_name}) if oo.affiliate_referral
end
column 'User' do |oo|
link_to(oo.sale.user.name, admin_user_path(oo.sale.user.id), {:title => oo.sale.user.name})
end
column 'When' do |oo|
oo.created_at
end
end
controller do
end
end

View File

@ -0,0 +1,9 @@
class JamRuby::GiftCard
attr_accessor :csv
def process_csv
end
end

View File

@ -308,4 +308,6 @@ aac_master.sql
video_recording.sql
web_playable_jamtracks.sql
affiliate_partner_rate.sql
track_downloads.sql
track_downloads.sql
jam_track_lang_idx.sql
giftcard.sql

13
db/up/giftcard.sql Normal file
View File

@ -0,0 +1,13 @@
CREATE TABLE gift_cards (
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
code VARCHAR(64) UNIQUE NOT NULL,
user_id VARCHAR (64) REFERENCES users(id) ON DELETE CASCADE,
card_type VARCHAR(64) NOT NULL,
origin VARCHAR(200),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX gift_card_user_id_idx ON gift_cards(user_id);
ALTER TABLE users ADD COLUMN gifted_jamtracks INTEGER DEFAULT 0;

View File

@ -0,0 +1 @@
CREATE INDEX ON jam_tracks(language);

View File

@ -58,4 +58,4 @@ CREATE INDEX jam_track_rights_updated ON jam_track_rights(updated_at);
CREATE INDEX jam_track_mixdown_packages_queued ON jam_track_mixdown_packages(queued);
CREATE INDEX jam_track_mixdown_packages_signing_queued ON jam_track_mixdown_packages(signing_queued_at);
CREATE INDEX jam_track_mixdown_packages_updated ON jam_track_mixdown_packages(updated_at);
CREATE INDEX jam_track_mixdown_packages_updated ON jam_track_mixdown_packages(updated_at);

View File

@ -252,6 +252,8 @@ require "jam_ruby/models/base_search"
require "jam_ruby/models/musician_search"
require "jam_ruby/models/band_search"
require "jam_ruby/import/tency_stem_mapping"
require "jam_ruby/models/jam_track_search"
require "jam_ruby/models/gift_card"
include Jampb

View File

@ -19,6 +19,8 @@ module ValidationMessages
# sessions
SESSION_NOT_FOUND = "Session not found."
NOT_FOUND = 'not found'
# genres
RECORDING_GENRE_LIMIT_EXCEEDED = "No more than 1 genre is allowed."
BAND_GENRE_LIMIT_EXCEEDED = "No more than 3 genres are allowed."

View File

@ -24,11 +24,33 @@ module JamRuby
end
def has_redeemable_jamtrack
raise "not a cookied anonymous user" if @cookies.nil?
APP_CONFIG.one_free_jamtrack_per_user && !@cookies[:redeemed_jamtrack]
end
def gifted_jamtracks
0
end
def free_jamtracks
if has_redeemable_jamtrack
1
else
0
end
end
def show_free_jamtrack?
ShoppingCart.user_has_redeemable_jam_track?(self)
end
def signup_hint
SignupHint.where(anonymous_user_id: @id).where('expires_at > ?', Time.now).first
end
def reload
end
end
end

View File

@ -102,11 +102,19 @@ module JamRuby
def self.search_target_class
end
def self.genre_ids
@@genre_ids ||= Hash[ *Genre.pluck(:id).collect { |v| [ v, v ] }.flatten ]
end
def self.instrument_ids
@@instrument_ids ||= Hash[ *Instrument.pluck(:id).collect { |v| [ v, v ] }.flatten ]
end
def _genres(rel, query_data=json)
gids = query_data[KEY_GENRES]
unless gids.blank?
allgids = Genre.order(:id).pluck(:id)
gids = gids.select { |gg| allgids.index(gg).present? }
allgids = self.class.genre_ids
gids = gids.select { |gg| allgids.has_key?(gg) }
unless gids.blank?
gidsql = gids.join("','")
@ -119,8 +127,8 @@ module JamRuby
def _instruments(rel, query_data=json)
unless (instruments = query_data[KEY_INSTRUMENTS]).blank?
instrids = Instrument.order(:id).pluck(:id)
instruments = instruments.select { |ii| instrids.index(ii['instrument_id']).present? }
instrids = self.class.instrument_ids
instruments = instruments.select { |ii| instrids.has_key?(ii['instrument_id']) }
unless instruments.blank?
instsql = "SELECT player_id FROM musicians_instruments WHERE (("

View File

@ -22,5 +22,13 @@ module JamRuby
def to_s
description
end
def self.jam_track_list
sql = "SELECT DISTINCT genre_id FROM genres_jam_tracks WHERE genre_id IS NOT NULL"
Genre.select("DISTINCT(genres.id), genres.*")
.where("genres.id IN (#{sql})")
.order('genres.description ASC, genres.id')
end
end
end

View File

@ -2,7 +2,10 @@ module JamRuby
class GenreJamTrack < ActiveRecord::Base
self.table_name = 'genres_jam_tracks'
belongs_to :jam_track, class_name: 'JamRuby::JamTrack'
belongs_to :genre, class_name: 'JamRuby::Genre'
attr_accessible :jam_track_id, :genre_id
belongs_to :jam_track, class_name: 'JamRuby::JamTrack', inverse_of: :genres_jam_tracks
belongs_to :genre, class_name: 'JamRuby::Genre', inverse_of: :genres_jam_tracks
end
end

View File

@ -0,0 +1,35 @@
module JamRuby
class GiftCard < ActiveRecord::Base
@@log = Logging.logger[GiftCard]
JAM_TRACKS_10 = 'jam_tracks_10'
JAM_TRACKS_20 = 'jam_tracks_20'
CARD_TYPES =
[
JAM_TRACKS_10,
JAM_TRACKS_20
]
belongs_to :user, class_name: "JamRuby::User"
validates :card_type, presence: true, inclusion: {in: CARD_TYPES}
validates :code, presence: true, uniqueness: true
after_save :check_gifted
def check_gifted
if user && user_id_changed?
if card_type == JAM_TRACKS_10
user.gifted_jamtracks += 10
elsif card_type == JAM_TRACKS_20
user.gifted_jamtracks += 20
else
raise "unknown card type #{card_type}"
end
user.save!
end
end
end
end

View File

@ -47,6 +47,12 @@ module JamRuby
return Instrument.where('instruments.popularity > 0').order('instruments.popularity DESC, instruments.description ASC')
end
def self.jam_track_list
sql = "SELECT DISTINCT instrument_id FROM jam_track_tracks WHERE instrument_id IS NOT NULL"
Instrument.where("instruments.id IN (#{sql})")
.order('instruments.description ASC')
end
def icon_name
MAP_ICON_NAME[self.id]
end

View File

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
module JamRuby
class JamTrack < ActiveRecord::Base
include JamRuby::S3ManagerMixin
@ -52,7 +53,7 @@ module JamRuby
belongs_to :licensor , class_name: 'JamRuby::JamTrackLicensor', foreign_key: 'licensor_id', :inverse_of => :jam_tracks
has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "jam_track_id"
has_many :genres_jam_tracks, :class_name => "JamRuby::GenreJamTrack", :foreign_key => "jam_track_id", inverse_of: :jam_track
has_many :genres, :through => :genres_jam_tracks, :class_name => "JamRuby::Genre", :source => :genre
has_many :jam_track_tracks, :class_name => "JamRuby::JamTrackTrack", order: 'track_type ASC, position ASC, part ASC, instrument_id ASC'
@ -255,12 +256,12 @@ module JamRuby
limit = options[:limit]
limit ||= 20
limit = limit.to_i
per_page = limit
else
limit = per_page
end
start = (page -1 )* per_page
limit = per_page
else
limit = options[:limit]
limit ||= 20
@ -340,6 +341,16 @@ module JamRuby
query = query.where("jam_track_tracks.instrument_id = '#{options[:instrument]}' and jam_track_tracks.track_type != 'Master'") unless options[:instrument].blank?
query = query.where("jam_tracks.sales_region = '#{options[:availability]}'") unless options[:availability].blank?
# FIXME: n+1 queries for rights and genres
# query = query.includes([{ jam_track_tracks: :instrument },
# :jam_track_tap_ins,
# :jam_track_rights,
# :genres])
# { genres_jam_tracks: :genre },
query = query.includes([{ jam_track_tracks: :instrument },
{ genres_jam_tracks: :genre },
:jam_track_tap_ins])
count = query.total_entries
if count == 0
@ -456,5 +467,9 @@ module JamRuby
self.slug = sluggarize(original_artist) + '-' + sluggarize(name)
end
def to_s
"#{self.name} (#{self.original_artist})"
end
end
end

View File

@ -0,0 +1,168 @@
module JamRuby
class JamTrackSearch < BaseSearch
cattr_accessor :jschema, :search_meta
attr_accessor :user_counters
KEY_QUERY = 'query'
KEY_SEARCH_STR = 'search_str'
KEY_RESULT_TYPES = 'result_types'
KEY_SONGS = 'songs'
KEY_ARTISTS = 'artists'
KEY_RESULTS = 'results'
KEY_RESULT_SETS = 'result_sets'
KEY_PAGE_NUM = 'page_num'
KEY_TOTAL_COUNT = 'total_count'
KEY_PAGE_COUNT = 'page_count'
KEY_PER_PAGE = 'per_page'
PER_PAGE = 'development'==Rails.env ? 8 : 20
KEY_GENRES = 'genres'
KEY_INSTRUMENTS = 'instruments'
KEY_LANGUAGE = 'language'
KEY_ORIGINAL_ARTIST = 'original_artist'
def self.json_schema
return @@jschema ||= {
KEY_QUERY => {
KEY_SEARCH_STR => '',
KEY_INSTRUMENTS => [],
KEY_GENRES => [],
KEY_LANGUAGE => '',
KEY_ORIGINAL_ARTIST => '',
KEY_RESULT_TYPES => [],
KEY_PAGE_NUM => 1,
KEY_PER_PAGE => PER_PAGE,
},
KEY_RESULT_SETS => {
KEY_SONGS => {
KEY_RESULTS => [],
KEY_PAGE_NUM => 1,
KEY_TOTAL_COUNT => 0,
KEY_PAGE_COUNT => 0,
},
KEY_ARTISTS => {
KEY_RESULTS => [],
KEY_PAGE_NUM => 1,
KEY_TOTAL_COUNT => 0,
KEY_PAGE_COUNT => 0,
},
},
}
end
def self.search_target_class
JamTrack
end
def do_search(query)
rel = JamTrack.unscoped
unless (gids = query[KEY_GENRES]).blank?
allgids = self.class.genre_ids
gids = gids.select { |gg| allgids.has_key?(gg) }
unless gids.blank?
sqlstr = "'#{gids.join("','")}'"
rel = rel.joins(:genres_jam_tracks)
rel = rel.where("genres_jam_tracks.genre_id IN (#{sqlstr})")
end
end
unless (instruments = query[KEY_INSTRUMENTS]).blank?
instrids = self.class.instrument_ids
instruments = instruments.select { |ii| instrids.has_key?(ii['instrument_id']) }
unless instruments.blank?
sqlstr = "'#{instruments.join("','")}'"
rel = rel.joins(:jam_track_tracks)
rel = rel.where("jam_track_tracks.instrument_id IN (#{sqlstr})")
rel = rel.where("jam_track_tracks.track_type != 'Master'")
end
end
unless (artist_name = query[KEY_ORIGINAL_ARTIST]).blank?
rel = rel.where(original_artist: artist_name)
end
rel
end
def search_results_page(query=nil)
filter = {
KEY_QUERY => query,
}
result_types = query[KEY_RESULT_TYPES]
if result_types
has_songs, has_artists = result_types.index(KEY_SONGS), result_types.index(KEY_ARTISTS)
else
has_songs, has_artists = true, true
end
result_sets = filter[KEY_RESULT_SETS] = self.class.json_schema[KEY_RESULT_SETS].clone
if has_songs
rel = do_search(query)
unless (val = query[KEY_SEARCH_STR]).blank?
tsquery = Search.create_tsquery(val)
rel = rel.where("(search_tsv @@ to_tsquery('jamenglish', ?))", tsquery) if tsquery
end
rel = rel.order(:name).includes(:genres)
pgnum = [query[KEY_PAGE_NUM].to_i, 1].max
rel = rel.paginate(:page => pgnum, :per_page => query[KEY_PER_PAGE])
results = rel.all.collect do |jt|
{
'id' => jt.id,
'name' => jt.name,
'artist' => jt.original_artist,
'genre' => jt.genres.map(&:description).join(', '),
'plan_code' => jt.plan_code,
'year' => jt.year
}
end
result_sets[KEY_SONGS] = {
KEY_RESULTS => results,
KEY_PAGE_NUM => pgnum,
KEY_TOTAL_COUNT => rel.total_entries,
KEY_PAGE_COUNT => rel.total_pages,
}
end
if has_artists
rel = do_search(query)
counter = rel.select("DISTINCT(jam_tracks.original_artist)")
rel = rel.select("DISTINCT ON(jam_tracks.original_artist) jam_tracks.id, jam_tracks.original_artist")
unless (val = query[KEY_SEARCH_STR]).blank?
rel = rel.where("original_artist LIKE ?","%#{val}%")
counter = counter.where("original_artist LIKE ?","%#{val}%")
end
rel = rel.order(:original_artist)
pgnum = [query[KEY_PAGE_NUM].to_i, 1].max
rel = rel.paginate(:page => pgnum, :per_page => query[KEY_PER_PAGE])
results = rel.all.collect do |jt|
{ 'id' => jt.id, 'artist' => jt.original_artist }
end
artist_count = counter.count
result_sets[KEY_ARTISTS] = {
KEY_RESULTS => results,
KEY_PAGE_NUM => pgnum,
KEY_TOTAL_COUNT => artist_count,
KEY_PAGE_COUNT => (artist_count / query[KEY_PER_PAGE].to_f).ceil,
}
end
filter
end
def self.all_languages
JamTrack.select("SELECT DISTINCT(language)").order(:language).collect do |lang|
{ description: ISO_639.find_by_code(lang), id: lang }
end
end
end
end

View File

@ -129,7 +129,29 @@ module JamRuby
end
def self.is_only_freebie(shopping_carts_jam_tracks)
shopping_carts_jam_tracks.length == 1 && shopping_carts_jam_tracks[0].product_info[:free]
free = true
shopping_carts_jam_tracks.each do |cart|
free = cart.product_info[:free]
if !free
break
end
end
free
end
# we don't allow mixed shopping carts :/
def self.is_mixed(shopping_carts)
free = false
non_free = false
shopping_carts.each do |cart|
if cart.product_info[:free]
free = true
else
non_free = true
end
end
free && non_free
end
# this method will either return a valid sale, or throw a RecurlyClientError or ActiveRecord validation error (save! failed)
@ -311,10 +333,17 @@ module JamRuby
jam_track_right.redeemed = shopping_cart.free?
end
# also if the purchase was a free one, then update the user record to no longer allow redeemed jamtracks
# also if the purchase was a free one, then:
# first, mark the free has_redeemable_jamtrack field if that's still true
# and if still they have more free things, then redeem the giftable_jamtracks
if shopping_cart.free?
User.where(id: current_user.id).update_all(has_redeemable_jamtrack: false)
current_user.has_redeemable_jamtrack = false # make sure model reflects the truth
if user.has_redeemable_jamtrack
User.where(id: current_user.id).update_all(has_redeemable_jamtrack: false)
current_user.has_redeemable_jamtrack = false
else
User.where(id: current_user.id).update_all(gifted_jamtracks: current_user.gifted_jamtracks - 1)
current_user.gifted_jamtracks = current_user.gifted_jamtracks - 1
end
end

View File

@ -12,6 +12,8 @@ module JamRuby
attr_accessible :quantity, :cart_type, :product_info
attr_accessor :skip_mix_check
validates_uniqueness_of :cart_id, scope: [:cart_type, :user_id, :anonymous_user_id]
belongs_to :user, :inverse_of => :shopping_carts, :class_name => "JamRuby::User", :foreign_key => "user_id"
@ -20,6 +22,7 @@ module JamRuby
validates :cart_type, presence: true
validates :cart_class_name, presence: true
validates :marked_for_redeem, numericality: {only_integer: true}
validate :not_mixed
default_scope order('created_at DESC')
@ -38,6 +41,31 @@ module JamRuby
(quantity - marked_for_redeem) * product.price
end
def not_mixed
return if @skip_mix_check
existing_carts = []
this_user = any_user()
if this_user
existing_carts = this_user.shopping_carts
end
existing_carts = existing_carts.to_a
existing_carts << self
if Sale.is_mixed(existing_carts)
if free?
errors.add(:base, "You can not add a free JamTrack to a cart with non-free items. Please clear out your cart.")
return false
else
errors.add(:base, "You can not add a non-free JamTrack to a cart containing free items. Please clear out your cart.")
return false
end
end
false
end
def cart_product
self.cart_class_name.classify.constantize.find_by_id(self.cart_id) unless self.cart_class_name.blank?
@ -51,6 +79,16 @@ module JamRuby
marked_for_redeem == quantity
end
def any_user
if user
user
elsif anonymous_user_id
AnonymousUser.new(anonymous_user_id, nil)
else
nil
end
end
def self.create user, product, quantity = 1, mark_redeem = false
cart = ShoppingCart.new
if user.is_a?(User)
@ -81,6 +119,7 @@ module JamRuby
if free?
puts "GOT A FREEBIE!"
# create the credit, then the pseudo charge
[
{
@ -134,28 +173,32 @@ module JamRuby
# if no shpping carts have been marked, then mark it redeemable
# should be wrapped in a TRANSACTION
def self.user_has_redeemable_jam_track?(any_user)
mark_redeem = false
if APP_CONFIG.one_free_jamtrack_per_user && any_user.has_redeemable_jamtrack
mark_redeem = true # start out assuming we can redeem...
if any_user.has_redeemable_jamtrack || any_user.gifted_jamtracks > 0
free_in_cart = 0
any_user.shopping_carts.each do |shopping_cart|
# but if we find any shopping cart item already marked for redeem, then back out of mark_redeem=true
if shopping_cart.cart_type == JamTrack::PRODUCT_TYPE && shopping_cart.marked_for_redeem > 0
mark_redeem = false
break
if shopping_cart.cart_type == JamTrack::PRODUCT_TYPE
free_in_cart += shopping_cart.marked_for_redeem
end
end
any_user.free_jamtracks > free_in_cart
else
false
end
mark_redeem
end
# adds a jam_track to cart, checking for promotions
def self.add_jam_track_to_cart(any_user, jam_track)
def self.add_jam_track_to_cart(any_user, jam_track, clear:false)
cart = nil
ShoppingCart.transaction do
if any_user.has_redeemable_jamtrack
# if you still have a freebie available to you, or if you are an anonymous user, we make sure there is nothing else in your shopping cart
if clear
# if you are an anonymous user, we make sure there is nothing else in your shopping cart ... keep it clean for the 'new user rummaging around for a freebie scenario'
any_user.destroy_all_shopping_carts
any_user.reload
end
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
@ -168,19 +211,48 @@ module JamRuby
def self.remove_jam_track_from_cart(any_user, cart)
ShoppingCart.transaction do
cart.destroy
# check if we should move the redemption
# so that user.shopping_carts reflects truth
any_user.reload
# check if we should move the redemption around automatically
mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)
carts = any_user.shopping_carts
# if we find any carts on the account, mark one redeemable
# if we find any carts on the account that are not redeemed, mark first one redeemable
if mark_redeem && carts.length > 0
carts[0].redeem(mark_redeem)
carts[0].save
carts.each do |cart|
if cart.marked_for_redeem == 0
if cart.quantity > 1
raise 'unknown situation for redeemption juggling'
end
cart.redeem(mark_redeem)
cart.save
break
end
end
end
end
end
# if the number of items in the shopping cart is less than gifted_jamtracks on the user, then fix them all up
def self.apply_gifted_jamtracks(user)
jam_track_carts = user.shopping_carts.where(cart_type:JamTrack::PRODUCT_TYPE)
if jam_track_carts.count > user.gifted_jamtracks
# just whack everything in their shopping cart
user.destroy_all_shopping_carts
return
end
jam_track_carts.each do |cart|
cart.skip_mix_check = true
cart.marked_for_redeem = 1
cart.save!
end
end
def port(user, anonymous_user)
ShoppingCart.transaction do

View File

@ -40,7 +40,7 @@ module JamRuby
attr_accessible :first_name, :last_name, :email, :city, :password, :password_confirmation, :state, :country, :birth_date, :subscribe_email, :terms_of_service, :original_fpfile, :cropped_fpfile, :cropped_large_fpfile, :cropped_s3_path, :cropped_large_s3_path, :photo_url, :large_photo_url, :crop_selection
# updating_password corresponds to a lost_password
attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field, :mods_json
attr_accessor :updating_password, :updating_email, :updated_email, :update_email_confirmation_url, :administratively_created, :current_password, :setting_password, :confirm_current_password, :updating_avatar, :updating_progression_field, :mods_json, :expecting_gift_card
belongs_to :icecast_server_group, class_name: "JamRuby::IcecastServerGroup", inverse_of: :users, foreign_key: 'icecast_server_group_id'
@ -148,6 +148,9 @@ module JamRuby
# events
has_many :event_sessions, :class_name => "JamRuby::EventSession"
# gift cards
has_many :gift_cards, :class_name=> "JamRuby::GiftCard"
# affiliate_partner
has_one :affiliate_partner, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :partner_user_id, inverse_of: :partner_user
belongs_to :affiliate_referral, :class_name => "JamRuby::AffiliatePartner", :foreign_key => :affiliate_referral_id, :counter_cache => :referral_user_count
@ -194,6 +197,7 @@ module JamRuby
validates :terms_of_service, :acceptance => {:accept => true, :on => :create, :allow_nil => false }
validates :reuse_card, :inclusion => {:in => [true, false]}
validates :has_redeemable_jamtrack, :inclusion => {:in => [true, false]}
validates :gifted_jamtracks, presence: true, :numericality => { :less_than_or_equal_to => 100 }
validates :subscribe_email, :inclusion => {:in => [nil, true, false]}
validates :musician, :inclusion => {:in => [true, false]}
validates :show_whats_next, :inclusion => {:in => [nil, true, false]}
@ -214,6 +218,7 @@ module JamRuby
validate :email_case_insensitive_uniqueness
validate :update_email_case_insensitive_uniqueness, :if => :updating_email
validate :validate_mods
validate :presence_gift_card, :if => :expecting_gift_card
scope :musicians, where(:musician => true)
scope :fans, where(:musician => false)
@ -233,6 +238,18 @@ module JamRuby
end
end
def has_any_free_jamtracks
has_redeemable_jamtrack || gifted_jamtracks > 0
end
def free_jamtracks
(has_redeemable_jamtrack ? 1 : 0) + gifted_jamtracks
end
def show_free_jamtrack?
ShoppingCart.user_has_redeemable_jam_track?(self)
end
def failed_qualification(reason)
self.last_failed_certified_gear_at = DateTime.now
self.last_failed_certified_gear_reason = reason
@ -255,6 +272,12 @@ module JamRuby
end
end
def presence_gift_card
if self.gift_cards.length == 0
errors.add(:gift_card, ValidationMessages::NOT_FOUND)
end
end
def validate_current_password
# checks if the user put in their current password (used when changing your email, for instance)
errors.add(:current_password, ValidationMessages::NOT_YOUR_PASSWORD) if should_confirm_existing_password? && !valid_password?(self.current_password)
@ -1025,6 +1048,7 @@ module JamRuby
reuse_card = options[:reuse_card]
signup_hint = options[:signup_hint]
affiliate_partner = options[:affiliate_partner]
gift_card = options[:gift_card]
user = User.new
@ -1036,6 +1060,9 @@ module JamRuby
user.terms_of_service = terms_of_service
user.musician = musician
user.reuse_card unless reuse_card.nil?
user.gifted_jamtracks = 0
user.has_redeemable_jamtrack = true
# FIXME: Setting random password for social network logins. This
# is because we have validations all over the place on this.
@ -1140,8 +1167,22 @@ module JamRuby
end
end
found_gift_card = nil
# if a gift card value was passed in, then try to find that gift card and apply it to user
if gift_card
user.expecting_gift_card = true
found_gift_card = GiftCard.where(code:gift_card).where(user_id:nil).first
user.gift_cards << found_gift_card if found_gift_card
end
user.save
if found_gift_card
user.reload
ShoppingCart.apply_gifted_jamtracks(user)
end
# if the user has just one, free jamtrack in their shopping cart, and it matches the signup hint, then auto-buy it
# only_freebie_in_cart =
# signup_hint &&
@ -1181,6 +1222,7 @@ module JamRuby
end
end
end
user.reload if user.id# gift card adding gifted_jamtracks doesn't reflect here until reload
user
end # def signup

View File

@ -19,6 +19,8 @@ FactoryGirl.define do
terms_of_service true
last_jam_audio_latency 5
reuse_card true
has_redeemable_jamtrack true
gifted_jamtracks 0
#u.association :musician_instrument, factory: :musician_instrument, user: u
@ -858,4 +860,9 @@ FactoryGirl.define do
legalese Faker::Lorem.paragraphs(6).join("\n\n")
end
factory :gift_card, class: 'JamRuby::GiftCard' do
sequence(:code) {n.to_s}
card_type = JamRuby::GiftCard::JAM_TRACKS_10
end
end

View File

@ -212,6 +212,7 @@ describe 'Band Search Model' do
expect(search.results[0].id).to eq(band.id)
end
it "filters by genre" do
pending
band_id = band.id
filter[BandSearch::KEY_GENRES] = [band_id]
search.search_results_page(BandSearch::TO_JOIN, filter)

View File

@ -42,28 +42,28 @@ describe JamTrackMixdown do
end
it "validates speed numeric" do
invalid = FactoryGirl.build(:jam_track_mixdown, settings: {"speed": "5"}.to_json)
invalid = FactoryGirl.build(:jam_track_mixdown, settings: {"speed" => "5"}.to_json)
invalid.save
invalid.errors.any?.should be_true
invalid.errors["settings"].should eq(["has non-integer speed"])
end
it "validates pitch numeric" do
invalid = FactoryGirl.build(:jam_track_mixdown, settings: {"pitch": "5"}.to_json)
invalid = FactoryGirl.build(:jam_track_mixdown, settings: {"pitch" => "5"}.to_json)
invalid.save
invalid.errors.any?.should be_true
invalid.errors["settings"].should eq(["has non-integer pitch"])
end
it "validates speed not-float" do
invalid = FactoryGirl.build(:jam_track_mixdown, settings: {"speed": 5.5}.to_json)
invalid = FactoryGirl.build(:jam_track_mixdown, settings: {"speed" => 5.5}.to_json)
invalid.save
invalid.errors.any?.should be_true
invalid.errors["settings"].should eq(["has non-integer speed"])
end
it "validates pitch not-float" do
invalid = FactoryGirl.build(:jam_track_mixdown, settings: {"pitch": 10.5}.to_json)
invalid = FactoryGirl.build(:jam_track_mixdown, settings: {"pitch" => 10.5}.to_json)
invalid.save
invalid.errors.any?.should be_true
invalid.errors["settings"].should eq(["has non-integer pitch"])

View File

@ -0,0 +1,89 @@
require 'spec_helper'
describe 'JamTrack Search Model' do
let(:artist_filter) {
filter = JamTrackSearch.json_schema.clone
filter[JamTrackSearch::KEY_RESULT_TYPES] = [JamTrackSearch::KEY_ARTISTS]
filter
}
let(:song_filter) {
filter = JamTrackSearch.json_schema.clone
filter[JamTrackSearch::KEY_RESULT_TYPES] = [JamTrackSearch::KEY_SONGS]
filter
}
let(:freebird) {
FactoryGirl.create(:jam_track_with_tracks, original_artist: 'jim bob', name: 'freebird')
}
let(:stairway) {
FactoryGirl.create(:jam_track_with_tracks, original_artist: 'jim bob', name: 'stairway to heaven')
}
before :each do
JamTrack.delete_all
JamTrackTrack.delete_all
freebird
stairway
end
describe "Search filter" do
it "finds by artist" do
pending
filter = artist_filter.clone
filter[JamTrackSearch::KEY_SEARCH_STR] = freebird.original_artist
filter = JamTrackSearch.new.search_results_page(filter['query'])
expect(filter[JamTrackSearch::KEY_RESULTS][JamTrackSearch::KEY_ARTISTS].count).to be(1)
end
it "paginates by artist" do
pending
JamTrackSearch::PER_PAGE.times do |nn|
FactoryGirl.create(:jam_track_with_tracks,
original_artist: freebird.original_artist + nn.to_s,
name: 'abc'+nn.to_s)
end
filter = artist_filter.clone
filter[JamTrackSearch::KEY_SEARCH_STR] = freebird.original_artist
out_filter = JamTrackSearch.new.search_results_page(filter.clone['query'])
expect(out_filter[JamTrackSearch::KEY_RESULTS][JamTrackSearch::KEY_ARTISTS].count).to be([JamTrackSearch::PER_PAGE, JamTrack.count].min)
num_page = (JamTrack.count / JamTrackSearch::PER_PAGE) + 1
expect(out_filter[JamTrackSearch::KEY_ARTISTS]['page_count']).to be(num_page)
filter[JamTrackSearch::KEY_ARTISTS]['page_num'] = 2
out_filter = JamTrackSearch.new.search_results_page(filter.clone['query'])
expect(out_filter[JamTrackSearch::KEY_RESULTS][JamTrackSearch::KEY_ARTISTS].count).to be(1)
end
it "finds by song" do
pending
filter = song_filter.clone
filter[JamTrackSearch::KEY_SEARCH_STR] = freebird.name
filter = JamTrackSearch.new.search_results_page(filter.clone['query'])
expect(filter[JamTrackSearch::KEY_RESULTS][JamTrackSearch::KEY_SONGS].count).to be(1)
end
it "paginates by song" do
pending
(JamTrackSearch::PER_PAGE + 2).times do |nn|
FactoryGirl.create(:jam_track_with_tracks,
original_artist: freebird.original_artist,
name: 'abc'+nn.to_s)
end
filter = song_filter.clone
filter[JamTrackSearch::KEY_SEARCH_STR] = 'abc'
out_filter = JamTrackSearch.new.search_results_page(filter.clone['query'])
expect(out_filter[JamTrackSearch::KEY_RESULTS][JamTrackSearch::KEY_SONGS].count).to be([JamTrackSearch::PER_PAGE, JamTrack.count].min)
total_count = JamTrack.where("name LIKE 'abc%'").count
num_page = (total_count / JamTrackSearch::PER_PAGE) + (0==(total_count % JamTrackSearch::PER_PAGE) ? 0 : 1)
expect(out_filter[JamTrackSearch::KEY_SONGS]['page_count']).to be(num_page)
filter[JamTrackSearch::KEY_SONGS]['page_num'] = 2
out_filter = JamTrackSearch.new.search_results_page(filter.clone['query'])
expect(out_filter[JamTrackSearch::KEY_RESULTS][JamTrackSearch::KEY_SONGS].count).to be(2)
end
end
end

View File

@ -238,6 +238,7 @@ describe 'Musician Search Model' do
end
it "sorts by latency", intermittent: true do
pending
search.update_json_value(MusicianSearch::KEY_SORT_ORDER, MusicianSearch::SORT_VALS[0])
results = search.do_search
expect(results[0].id).to eq(@user1.id) # HAS FAILED HERE TOO

View File

@ -5,6 +5,23 @@ describe Sale do
let(:user) {FactoryGirl.create(:user)}
let(:user2) {FactoryGirl.create(:user)}
let(:jam_track) {FactoryGirl.create(:jam_track)}
let(:jam_track2) {FactoryGirl.create(:jam_track)}
let(:jam_track3) {FactoryGirl.create(:jam_track)}
def assert_free_line_item(sale_line_item, jamtrack)
sale_line_item.recurly_tax_in_cents.should be_nil
sale_line_item.recurly_total_in_cents.should be_nil
sale_line_item.recurly_currency.should be_nil
sale_line_item.recurly_discount_in_cents.should be_nil
sale_line_item.product_type.should eq(JamTrack::PRODUCT_TYPE)
sale_line_item.unit_price.should eq(jamtrack.price)
sale_line_item.quantity.should eq(1)
sale_line_item.free.should eq(1)
sale_line_item.sales_tax.should be_nil
sale_line_item.shipping_handling.should eq(0)
sale_line_item.recurly_plan_code.should eq(jamtrack.plan_code)
sale_line_item.product_id.should eq(jamtrack.id)
end
describe "index" do
it "empty" do
@ -47,6 +64,9 @@ describe Sale do
let(:user) {FactoryGirl.create(:user)}
let(:jamtrack) { FactoryGirl.create(:jam_track) }
let(:jamtrack2) { FactoryGirl.create(:jam_track) }
let(:jamtrack3) { FactoryGirl.create(:jam_track) }
let(:jamtrack4) { FactoryGirl.create(:jam_track) }
let(:jam_track_price_in_cents) { (jamtrack.price * 100).to_i }
let(:client) { RecurlyClient.new }
let(:billing_info) {
@ -87,6 +107,7 @@ describe Sale do
sales.should eq(user.sales)
sale = sales[0]
sale.recurly_invoice_id.should be_nil
sale.recurly_subtotal_in_cents.should eq(0)
@ -132,6 +153,92 @@ describe Sale do
user.has_redeemable_jamtrack.should be_false
end
it "for two jam tracks (1 freebie, 1 gifted), then 1 gifted/1 pay" do
user.gifted_jamtracks = 2
user.save!
shopping_cart1 = ShoppingCart.create user, jamtrack, 1, true
shopping_cart2 = ShoppingCart.create user, jamtrack2, 1, true
client.find_or_create_account(user, billing_info)
sales = Sale.place_order(user, [shopping_cart1, shopping_cart2])
user.reload
user.sales.length.should eq(1)
sale = sales[0]
sale.reload
sale.recurly_invoice_id.should be_nil
sale.recurly_subtotal_in_cents.should eq(0)
sale.recurly_tax_in_cents.should eq(0)
sale.recurly_total_in_cents.should eq(0)
sale.recurly_currency.should eq('USD')
sale.order_total.should eq(0)
sale.sale_line_items.length.should == 2
assert_free_line_item(sale.sale_line_items[0], jamtrack)
assert_free_line_item(sale.sale_line_items[1], jamtrack2)
# verify jam_track_rights data
right1 = JamTrackRight.where(user_id: user.id).where(jam_track_id: jamtrack.id).first
right2 = JamTrackRight.where(user_id: user.id).where(jam_track_id: jamtrack2.id).first
user.jam_track_rights.should have(2).items
right1.redeemed.should be_true
right2.redeemed.should be_true
user.has_redeemable_jamtrack.should be_false
user.gifted_jamtracks.should eq(1)
# OK! Now make a second purchase; this time, buy one free, one not free
shopping_cart3 = ShoppingCart.create user, jamtrack3, 1, true
shopping_cart4 = ShoppingCart.create user, jamtrack4, 1, false
client.find_or_create_account(user, billing_info)
sales = Sale.place_order(user, [shopping_cart3, shopping_cart4])
user.reload
user.sales.length.should eq(2)
sale = sales[0]
sale.reload
sale.recurly_invoice_id.should_not be_nil
sale.recurly_subtotal_in_cents.should eq(0)
sale.recurly_tax_in_cents.should eq(0)
sale.recurly_total_in_cents.should eq(0)
sale.recurly_currency.should eq('USD')
sale.order_total.should eq(0)
sale.sale_line_items.length.should == 2
assert_free_line_item(sale.sale_line_items[0], jamtrack3)
paid_right = JamTrackRight.where(user_id:user.id).where(jam_track_id: jamtrack4.id).first
sale_line_item.recurly_total_in_cents.should eq(jam_track_price_in_cents)
sale_line_item.recurly_currency.should eq('USD')
sale_line_item.recurly_discount_in_cents.should eq(0)
sale_line_item.product_type.should eq(JamTrack::PRODUCT_TYPE)
sale_line_item.unit_price.should eq(jamtrack4.price)
sale_line_item.quantity.should eq(1)
sale_line_item.free.should eq(0)
sale_line_item.sales_tax.should be_nil
sale_line_item.shipping_handling.should eq(0)
sale_line_item.recurly_plan_code.should eq(jamtrack4.plan_code)
sale_line_item.product_id.should eq(jamtrack.id)
sale_line_item.recurly_subscription_uuid.should be_nil
sale_line_item.recurly_adjustment_uuid.should_not be_nil
sale_line_item.recurly_adjustment_credit_uuid.should be_nil
sale_line_item.recurly_adjustment_uuid.should eq(paid_right.recurly_adjustment_uuid)
user.has_redeemable_jamtrack.should be_false
user.gifted_jamtracks.should eq(0)
end
it "for a free jam track with an affiliate association" do
partner = FactoryGirl.create(:affiliate_partner)
user.affiliate_referral = partner

View File

@ -3,8 +3,13 @@ require 'spec_helper'
describe ShoppingCart do
let(:user) { FactoryGirl.create(:user) }
let(:jam_track) {FactoryGirl.create(:jam_track) }
let(:jam_track2) {FactoryGirl.create(:jam_track) }
let(:jam_track) { FactoryGirl.create(:jam_track) }
let(:jam_track2) { FactoryGirl.create(:jam_track) }
let(:jam_track3) { FactoryGirl.create(:jam_track) }
let(:jam_track4) { FactoryGirl.create(:jam_track) }
let(:jam_track5) { FactoryGirl.create(:jam_track) }
let(:jam_track6) { FactoryGirl.create(:jam_track) }
let(:jam_track7) { FactoryGirl.create(:jam_track) }
before(:each) do
ShoppingCart.delete_all
@ -13,6 +18,9 @@ describe ShoppingCart do
it "can reference a shopping cart" do
shopping_cart = ShoppingCart.create user, jam_track, 1
shopping_cart.errors.any?.should be_false
shopping_cart.valid?.should be_true
user.reload
ShoppingCart.count.should == 1
user.shopping_carts.count.should == 1
user.shopping_carts[0].product_info[:name].should == jam_track.name
@ -22,16 +30,16 @@ describe ShoppingCart do
end
it "maintains only one fre JamTrack in ShoppingCart" do
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
it "maintains only one free JamTrack in ShoppingCart" do
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track, clear: true)
cart1.should_not be_nil
cart1.errors.any?.should be_false
user.reload
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track, clear: true)
cart2.errors.any?.should be_false
user.reload
user.shopping_carts.length.should eq(1)
cart3 = ShoppingCart.add_jam_track_to_cart(user, jam_track2)
cart3 = ShoppingCart.add_jam_track_to_cart(user, jam_track2, clear: true)
cart3.errors.any?.should be_false
user.reload
user.shopping_carts.length.should eq(1)
@ -53,19 +61,131 @@ describe ShoppingCart do
it "removes redeemable item to shopping cart (maintains only one in cart)" do
user.has_redeemable_jamtrack.should be_true
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track, clear: true)
cart1.should_not be_nil
cart1.errors.any?.should be_false
cart1.marked_for_redeem.should eq(1)
user.reload
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track2, clear: true)
cart2.should_not be_nil
cart2.errors.any?.should be_false
cart2.marked_for_redeem.should eq(1)
ShoppingCart.find_by_id(cart1.id).should be nil
ShoppingCart.remove_jam_track_from_cart(user, cart2)
user.reload
user.shopping_carts.length.should eq(0)
ShoppingCart.find_by_id(cart2.id).should be nil
end
end
describe "multiple free jamtracks" do
before(:each) do
user.gifted_jamtracks = 5
user.save!
end
it "user can add and remove jamtracks without issue, until 'mixed' free/non-free is hit" do
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
cart1.should_not be_nil
cart1.errors.any?.should be_false
user.reload
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track2)
cart2.should_not be_nil
cart2.errors.any?.should be_false
user.reload
user.shopping_carts.length.should eq(2)
cart1.marked_for_redeem.should eq(1)
cart2.marked_for_redeem.should eq(1)
ShoppingCart.remove_jam_track_from_cart(user, jam_track)
user.shopping_carts.length.should eq(0)
cart2.reload
cart3 = ShoppingCart.add_jam_track_to_cart(user, jam_track3)
cart3.errors.any?.should be_false
user.reload
user.shopping_carts.length.should eq(3)
cart1.marked_for_redeem.should eq(1)
cart2.marked_for_redeem.should eq(1)
cart3.marked_for_redeem.should eq(1)
cart4 = ShoppingCart.add_jam_track_to_cart(user, jam_track4)
cart4.errors.any?.should be_false
user.reload
user.shopping_carts.length.should eq(4)
cart1.marked_for_redeem.should eq(1)
cart2.marked_for_redeem.should eq(1)
cart3.marked_for_redeem.should eq(1)
cart4.marked_for_redeem.should eq(1)
cart5 = ShoppingCart.add_jam_track_to_cart(user, jam_track5)
cart5.errors.any?.should be_false
user.reload
user.shopping_carts.length.should eq(5)
cart1.marked_for_redeem.should eq(1)
cart2.marked_for_redeem.should eq(1)
cart3.marked_for_redeem.should eq(1)
cart4.marked_for_redeem.should eq(1)
cart5.marked_for_redeem.should eq(1)
cart6 = ShoppingCart.add_jam_track_to_cart(user, jam_track6)
cart6.errors.any?.should be_false
user.reload
user.shopping_carts.length.should eq(6)
cart1.marked_for_redeem.should eq(1)
cart2.marked_for_redeem.should eq(1)
cart3.marked_for_redeem.should eq(1)
cart4.marked_for_redeem.should eq(1)
cart5.marked_for_redeem.should eq(1)
cart6.marked_for_redeem.should eq(1)
cart7 = ShoppingCart.add_jam_track_to_cart(user, jam_track7)
cart7.errors.any?.should be_true
user.reload
user.shopping_carts.length.should eq(6)
cart1.marked_for_redeem.should eq(1)
cart2.marked_for_redeem.should eq(1)
cart3.marked_for_redeem.should eq(1)
cart4.marked_for_redeem.should eq(1)
cart5.marked_for_redeem.should eq(1)
cart6.marked_for_redeem.should eq(1)
end
end
describe "mixed" do
it "non-free then free" do
# you shouldn't be able to add a free after a non-free
user.has_redeemable_jamtrack = false
user.save!
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
cart1.should_not be_nil
cart1.errors.any?.should be_false
user.has_redeemable_jamtrack = true
user.save!
user.reload
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track2)
cart2.errors.any?.should be_true
cart2.errors[:base].should eq(["You can not add a free JamTrack to a cart with non-free items. Please clear out your cart."])
user.shopping_carts.length.should eq(1)
end
it "free then non-free" do
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
cart1.should_not be_nil
cart1.errors.any?.should be_false
user.reload
cart2 = ShoppingCart.add_jam_track_to_cart(user, jam_track2)
cart2.errors.any?.should be_true
cart2.errors[:base].should eq(["You can not add a non-free JamTrack to a cart containing free items. Please clear out your cart."])
user.shopping_carts.length.should eq(1)
end
end
end

View File

@ -94,8 +94,6 @@ gem 'bower-rails', "~> 0.9.2"
gem 'react-rails', '~> 1.0'
#gem "browserify-rails", "~> 0.7"
gem 'react-rails-img'
source 'https://rails-assets.org' do
gem 'rails-assets-reflux'
gem 'rails-assets-classnames'

View File

@ -3,4 +3,3 @@ Jasmine Javascript Unit Tests
Open browser to localhost:3000/teaspoon

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@ -95,7 +95,7 @@
$reuseExistingCardChk.iCheck(userDetail.reuse_card && userDetail.has_recurly_account ? 'check' : 'uncheck').attr('checked', userDetail.reuse_card)
// show appropriate prompt text based on whether user has a free jamtrack
if(user.free_jamtrack) {
if(user.has_redeemable_jamtrack) {
$freeJamTrackPrompt.removeClass('hidden')
}
else {

View File

@ -55,6 +55,16 @@ class CheckoutUtils
return carts[0].product_info.free
hasOnlyFreeItemsInShoppingCart: (carts) =>
if carts.length == 0
return false
for cart in carts
if !cart.product_info.free
return false
return true
configureRecurly: () =>
unless @configuredRecurly
context.recurly.configure(gon.global.recurly_public_api_key)

View File

@ -50,7 +50,7 @@
$browserJamTrackBtn.click(function() {
app.layout.closeDialog('getting-started')
window.location = '/client#/jamtrack/search'
window.location = '/client#/jamtrack'
return false;
})
@ -69,9 +69,9 @@
function beforeShow() {
app.user().done(function(user) {
var jamtrackRule = user.free_jamtrack ? 'has-free-jamtrack' : 'no-free-jamtrack'
var jamtrackRule = user.has_redeemable_jamtrack ? 'has-free-jamtrack' : 'no-free-jamtrack'
$jamTrackSection.removeClass('has-free-jamtrack').removeClass('no-free-jamtrack').addClass(jamtrackRule)
if(user.free_jamtrack) {
if(user.has_redeemable_jamtrack) {
$jamTracksLimitedTime.removeClass('hidden')
}
})

View File

@ -1187,6 +1187,7 @@
});
deferred.done(function(user) {
context.JK.currentUserFreeJamTrack = user.show_free_jamtrack
window.UserActions.loaded(user)
})
@ -1791,12 +1792,17 @@
}
function addJamtrackToShoppingCart(options) {
return $.ajax({
var deferred = $.ajax({
type: "POST",
url: '/api/shopping_carts/add_jamtrack?' + $.param(options),
dataType: "json",
contentType: 'application/json'
});
deferred.done(function(response) {
window.UserActions.modify(response)
})
return deferred
}
function getShoppingCarts() {
@ -1810,12 +1816,17 @@
}
function removeShoppingCart(options) {
return $.ajax({
var deferred = $.ajax({
type: "DELETE",
url: '/api/shopping_carts?' + $.param(options),
dataType: "json",
contentType: 'application/json'
})
deferred.done(function(response) {
window.UserActions.modify(response)
})
return deferred
}
function clearShoppingCart(options) {
@ -1986,6 +1997,17 @@
});
}
function redeemGiftCard(data) {
var id = getId(data);
return $.ajax({
type: "POST",
url: '/api/users/' + id + '/gift_cards',
dataType: "json",
contentType: 'application/json',
data: JSON.stringify(data),
});
}
function portOverCarts() {
return $.ajax({
type: "POST",
@ -2190,6 +2212,7 @@
this.playJamTrack = playJamTrack;
this.createSignupHint = createSignupHint;
this.createAlert = createAlert;
this.redeemGiftCard = redeemGiftCard;
this.signup = signup;
this.portOverCarts = portOverCarts;
return this;

View File

@ -1,484 +0,0 @@
$ = jQuery
context = window
context.JK ||= {}
context.JK.JamTrackScreen=class JamTrackScreen
LIMIT = 10
instrument_logo_map = context.JK.getInstrumentIconMap24()
constructor: (@app) ->
@EVENTS = context.JK.EVENTS
@logger = context.JK.logger
@screen = null
@content = null
@scroller = null
@genre = null
@artist = null
@instrument = null
@availability = null
@nextPager = null
@noMoreJamtracks = null
@currentPage = 0
@next = null
@currentQuery = this.defaultQuery()
@expanded = null
@shownHelperBubbles = false
beforeShow:(data) =>
this.setFilterFromURL()
if context.JK.currentUserId?
@app.user().done((user) =>
@user = user
this.refresh()
).fail((arg) =>
@logger.error("app.user.done failed: " + JSON.stringify(arg))
@logger.debug(arg.statusCode);
throw 'fail should not occur if user is available'
)
else
this.refresh()
unless @shownHelperBubbles
@shownHelperBubbles = true
@startHelperBubbles()
afterShow:(data) =>
context.JK.Tracking.jamtrackBrowseTrack(@app)
beforeHide: () =>
this.clearCtaHelpTimeout()
this.clearBandFilterHelpTimeout()
this.clearMasterHelpTimeout()
this.clearResults();
events:() =>
@genre.on 'change', this.search
@artist.on 'change', this.search
@instrument.on 'change', this.search
@availability.on 'change', this.search
clearResults:() =>
@currentPage = 0
@content.empty()
@noMoreJamtracks.hide()
@next = null
startHelperBubbles: () =>
@showBandFilterHelpTimeout = setTimeout(@showBandFilterHelp, 3500)
showBandFilterHelp: () =>
context.JK.HelpBubbleHelper.jamtrackBrowseBand(@artist.closest('.easydropdown-wrapper'), $('body'))
@showMasterHelpDueTime = new Date().getTime() + 11000 # 6000 ms for band tooltip to display, and 5 seconds of quiet time
@scroller.on('scroll', @masterHelpScrollWatch)
@scroller.on('scroll', @clearBubbles)
@showMasterHelpTimeout = setTimeout(@showMasterHelp, @masterHelpDueTime())
clearBubbles: () =>
if @helpBubble?
@helpBubble.btOff()
@helpBubble = null
# computes when we should show the master help bubble
masterHelpDueTime: () =>
dueTime = @showMasterHelpDueTime - new Date().getTime()
if dueTime <= 0
dueTime = 2000
dueTime
# computes when we should show the master help bubble
ctaHelpDueTime: () =>
dueTime = @showCtaHelpDueTime - new Date().getTime()
if dueTime <= 0
dueTime = 2000
dueTime
# if the user scrolls, reset the master help due time
masterHelpScrollWatch: () =>
@clearMasterHelpTimeout()
@showMasterHelpTimeout = setTimeout(@showMasterHelp, @masterHelpDueTime() + 2000)
# if the user scrolls, reset the master help due time
ctaHelpScrollWatch: () =>
@clearCtaHelpTimeout()
@showCtaHelpTimeout = setTimeout(@showCtaHelp, @ctaHelpDueTime() + 2000)
showCtaHelp: () =>
@scroller.off('scroll', @ctaHelpScrollWatch)
@clearCtaHelpTimeout()
cutoff = @scroller.offset().top;
@screen.find('.jamtrack-actions').each((i, element) =>
$element = $(element)
if ($element.offset().top >= cutoff)
@helpBubble = context.JK.HelpBubbleHelper.jamtrackBrowseCta($element, $('body'))
return false
else
return true
)
showMasterHelp: () =>
@scroller.off('scroll', @masterHelpScrollWatch)
@clearMasterHelpTimeout()
# don't show the help if the user has already clicked a preview
unless @userPreviewed
cutoff = @scroller.offset().top;
@screen.find('.jamtrack-preview[data-track-type="Master"]').each((i, element) =>
$element = $(element)
if ($element.offset().top >= cutoff)
@helpBubble = context.JK.HelpBubbleHelper.jamtrackBrowseMasterMix($element.find('.play-button'), $('body'))
return false
else
return true
)
@showCtaHelpDueTime = new Date().getTime() + 11000
@scroller.on('scroll', @ctaHelpScrollWatch)
@showCtaHelpTimeout = setTimeout(@showCtaHelp, @ctaHelpDueTime()) # 6000 ms for bubble show time, and 5000ms for delay
previewPlayed: () =>
@userPreviewed = true
clearCtaHelpTimeout:() =>
if @showCtaHelpTimeout?
clearTimeout(@showCtaHelpTimeout)
@showCtaHelpTimeout = null
clearBandFilterHelpTimeout: () =>
if @showBandFilterHelpTimeout?
clearTimeout(@showBandFilterHelpTimeout)
@showBandFilterHelpTimeout = null
clearMasterHelpTimeout: () =>
if @showMasterHelpTimeout?
clearTimeout(@showMasterHelpTimeout)
@showMasterHelpTimeout = null
setFilterFromURL:() =>
# Grab parms from URL for artist, instrument, and availability
parms=this.getParams()
if(parms.artist?)
@artist.val(parms.artist)
else
@artist.val('')
if(parms.instrument?)
@instrument.val(parms.instrument)
else
@instrument.val('')
if(parms.availability?)
@availability.val(parms.availability)
else
@availability.val('')
if window.history.replaceState #ie9 proofing
window.history.replaceState({}, "", "/client#/jamtrackBrowse")
getParams:() =>
params = {}
q = window.location.href.split("?")[1]
if q?
q = q.split('#')[0]
raw_vars = q.split("&")
for v in raw_vars
[key, val] = v.split("=")
params[key] = decodeURIComponent(val)
params
setFilterState: (state) =>
if state
@genre.easyDropDown('enable').removeAttr('disabled')
@artist.easyDropDown('enable').removeAttr('disabled')
@instrument.easyDropDown('enable').removeAttr('disabled')
@availability.easyDropDown('enable').removeAttr('disabled')
else
@genre.easyDropDown('disable').attr('disabled', 'disabled')
@artist.easyDropDown('disable').attr('disabled', 'disabled')
@instrument.easyDropDown('disable').attr('disabled', 'disabled')
@availability.easyDropDown('disable').attr('disabled', 'disabled')
refresh:() =>
this.clearResults()
@currentQuery = this.buildQuery()
that = this
this.setFilterState(false)
rest.getJamTracks(@currentQuery).done((response) =>
that.handleJamtrackResponse(response)
).fail( (jqXHR) =>
that.clearResults()
that.noMoreJamtracks.show()
that.app.notifyServerError jqXHR, 'Jamtrack Unavailable'
).always () =>
that.setFilterState(true)
search:() =>
this.refresh()
false
defaultQuery:() =>
query =
per_page: LIMIT
page: @currentPage+1
if @next
query.since = @next
query
buildQuery:() =>
@currentQuery = this.defaultQuery()
# genre filter
# var genres = @screen.find('#jamtrack_genre').val()
# if (genres !== undefined) {
# @currentQuery.genre = genres
# }
# instrument filter
instrument = @instrument.val()
if instrument?
@currentQuery.instrument = instrument
# artist filter
art = @artist.val()
if art?
@currentQuery.artist = art
# availability filter
availability = @availability.val()
if availability?
@currentQuery.availability = availability
@currentQuery
handleJamtrackResponse:(response) =>
@next = response.next
this.renderJamtracks(response)
if response.next == null
# if we less results than asked for, end searching
@scroller.infinitescroll 'pause'
if @currentPage == 0 and response.jamtracks.length == 0
@content.append '<td colspan="3" class="no-jamtracks-msg\'>No JamTracks found.</div>'
if @currentPage > 0
@noMoreJamtracks.show()
# there are bugs with infinitescroll not removing the 'loading'.
# it's most noticeable at the end of the list, so whack all such entries
$('.infinite-scroll-loader').remove()
else
@currentPage++
this.buildQuery()
this.registerInfiniteScroll()
registerInfiniteScroll:() =>
that = this
@scroller.infinitescroll {
behavior: 'local'
navSelector: '#jamtrackScreen .btn-next-pager'
nextSelector: '#jamtrackScreen .btn-next-pager'
binder: @scroller
dataType: 'json'
appendCallback: false
prefill: false
bufferPx: 100
loading:
msg: $('<div class="infinite-scroll-loader">Loading ...</div>')
img: '/assets/shared/spinner.gif'
path: (page) =>
'/api/jamtracks?' + $.param(that.buildQuery())
}, (json, opts) =>
this.handleJamtrackResponse(json)
@scroller.infinitescroll 'resume'
playJamtrack:(e) =>
e.preventDefault()
addToCartJamtrack:(e) =>
e.preventDefault()
$target = $(e.target)
params = id: $target.attr('data-jamtrack-id')
isFree = $(e.target).is('.is_free')
rest.addJamtrackToShoppingCart(params).done((response) =>
if(isFree)
if context.JK.currentUserId?
context.JK.currentUserFreeJamTrack = true # make sure the user sees no more free notices
context.location = '/client#/redeemComplete'
else
# now make a rest call to buy it
context.location = '/client#/redeemSignup'
else
context.location = '/client#/shoppingCart'
).fail @app.ajaxError
licenseUSWhy:(e) =>
e.preventDefault()
@app.layout.showDialog 'jamtrack-availability-dialog'
handleExpanded:(trackElement) =>
jamTrack = trackElement.data('jamTrack')
expanded = trackElement.data('expanded')
expand = !expanded
trackElement.data('expanded', expand)
detailArrow = trackElement.find('.jamtrack-detail-btn')
if expand
trackElement.find('.extra').removeClass('hidden')
detailArrow.html('hide tracks <a class="details-arrow arrow-up"></a>')
for track in jamTrack.tracks
trackElement.find("[jamtrack-track-id='#{track.id}']").removeClass('hidden')
else
trackElement.find('.extra').addClass('hidden')
detailArrow.html('show all tracks <a class="details-arrow arrow-down"></a>')
count = 0
for track in jamTrack.tracks
if count < 6
trackElement.find("[jamtrack-track-id='#{track.id}']").removeClass('hidden')
else
trackElement.find("[jamtrack-track-id='#{track.id}']").addClass('hidden')
count++
registerEvents:(parent) =>
#@screen.find('.jamtrack-detail-btn').on 'click', this.showJamtrackDescription
parent.find('.play-button').on 'click', this.playJamtrack
parent.find('.jamtrack-add-cart').on 'click', this.addToCartJamtrack
parent.find('.license-us-why').on 'click', this.licenseUSWhy
parent.find('.jamtrack-detail-btn').on 'click', this.toggleExpanded
# @screen.find('.jamtrack-preview').each (index, element) =>
# new JK.JamTrackPreview(data.app, $element, jamTrack, track, {master_shows_duration: true})
rerenderJamtracks:() =>
if @currentData?
@clearResults()
@renderJamtracks(@currentData)
false
computeWeight: (jam_track_track, instrument) =>
weight = switch
when jam_track_track.track_type == 'Master' then 0
when jam_track_track.instrument?.id == instrument then 1 + jam_track_track.position
else 10000 + jam_track_track.position
renderJamtracks:(data) =>
@currentData = data
that = this
for jamtrack in data.jamtracks
jamtrackExpanded = this.expanded==jamtrack.id
trackRow = _.clone(jamtrack)
trackRow.track_cnt = jamtrack.tracks.length
trackRow.tracks = []
# if an instrument is selected by the user, then re-order any jam tracks with a matching instrument to the top
instrument = @instrument.val()
if instrument?
jamtrack.tracks.sort((a, b) =>
aWeight = @computeWeight(a, instrument)
bWeight = @computeWeight(b, instrument)
return aWeight - bWeight
)
for track in jamtrack.tracks
trackRow.tracks.push(track)
if track.track_type=='Master'
track.instrument_desc = "Master"
else
inst = '../assets/content/icon_instrument_default24.png'
if track.instrument?
if track.instrument.id in instrument_logo_map
inst = instrument_logo_map[track.instrument.id].asset
track.instrument_desc = track.instrument.description
track.instrument_url = inst
if track.part != ''
track.instrument_desc += ' (' + track.part + ')'
free_state = if context.JK.currentUserFreeJamTrack then 'free' else 'non-free'
is_free = free_state == 'free'
options =
jamtrack: trackRow
expanded: false
free_state: free_state,
is_free: is_free
@jamtrackItem = $(context._.template($('#template-jamtrack').html(), options, variable: 'data'))
that.renderJamtrack(@jamtrackItem, jamtrack)
that.registerEvents(@jamtrackItem)
renderJamtrack:(jamtrackElement, jamTrack) =>
jamtrackElement.data('jamTrack', jamTrack)
jamtrackElement.data('expanded', true)
@content.append jamtrackElement
#if this.expanded==jamTrack.id
for track in jamTrack.tracks
trackRow = jamtrackElement.find("[jamtrack-track-id='#{track.id}']")
previewElement = trackRow.find(".jamtrack-preview")
preview = new JK.JamTrackPreview(@app, previewElement, jamTrack, track, {master_shows_duration: true, color:'gray'})
$(preview).on(@EVENTS.PREVIEW_PLAYED, @previewPlayed)
this.handleExpanded(jamtrackElement, false)
showJamtrackDescription:(e) =>
e.preventDefault()
@description = $(e.target).parent('.detail-arrow').next()
if @description.css('display') == 'none'
@description.show()
else
@description.hide()
toggleExpanded:(e) =>
e.preventDefault()
jamtrackRecord = $(e.target).parents('.jamtrack-record')
jamTrackId = jamtrackRecord.attr("jamtrack-id")
this.handleExpanded(jamtrackRecord)
initialize:() =>
screenBindings =
'beforeShow': this.beforeShow
'afterShow': this.afterShow
'beforeHide' : this.beforeHide
@app.bindScreen 'jamtrackBrowse', screenBindings
@screen = $('#jamtrack-find-form')
@scroller = @screen.find('.content-body-scroller')
@content = @screen.find('.jamtrack-content')
@genre = @screen.find('#jamtrack_genre')
@artist = @screen.find('#jamtrack_artist')
@instrument = @screen.find('#jamtrack_instrument')
@availability = @screen.find('#jamtrack_availability')
@nextPager = @screen.find('a.btn-next-pager')
@noMoreJamtracks = @screen.find('.end-of-jamtrack-list')
if @screen.length == 0
throw new Error('@screen must be specified')
if @scroller.length == 0
throw new Error('@scroller must be specified')
if @content.length == 0
throw new Error('@content must be specified')
if @noMoreJamtracks.length == 0
throw new Error('@noMoreJamtracks must be specified')
#if(@genre.length == 0) throw new Error("@genre must be specified")
if @artist.length == 0
throw new Error('@artist must be specified')
if @instrument.length == 0
throw new Error('@instrument must be specified')
#if @availability.length == 0
# throw new Error('@availability must be specified')
this.events()

View File

@ -1,80 +0,0 @@
$ = jQuery
context = window
context.JK ||= {}
context.JK.JamTrackLanding = class JamTrackLanding
constructor: (@app) ->
@rest = context.JK.Rest()
@client = context.jamClient
@logger = context.JK.logger
@screen = null
@noFreeJamTrack = null
@freeJamTrack = null
@bandList = null
@noBandsFound = null
initialize:() =>
screenBindings =
'beforeShow': @beforeShow
'afterShow': @afterShow
#@app.bindScreen('jamtrackLanding', screenBindings)
@screen = $('#jamtrackLanding')
@noFreeJamTrack = @screen.find('.no-free-jamtrack')
@freeJamTrack = @screen.find('.free-jamtrack')
@bandList = @screen.find('#band_list')
@noBandsFound = @screen.find('#no_bands_found')
beforeShow:() =>
@noFreeJamTrack.addClass('hidden')
@freeJamTrack.addClass('hidden')
afterShow:() =>
if context.JK.currentUserId
@app.user().done(@onUser)
else
@onUser({free_jamtrack: gon.global.one_free_jamtrack_per_user})
onUser:(user) =>
if user.free_jamtrack
@freeJamTrack.removeClass('hidden')
else
@noFreeJamTrack.removeClass('hidden')
# Get artist names and build links
@rest.getJamTrackArtists({group_artist: true, per_page:100})
.done(this.buildArtistLinks)
.fail(this.handleFailure)
# Bind links to action that will open the jam_tracks list view filtered to given artist_name:
# artist_name
this.bindArtistLinks()
buildArtistLinks:(response) =>
# Get artist names and build links
@logger.debug("buildArtest links response", response)
artists = response.artists
$("#band_list>li:not('#no_bands_found')").remove()
if artists.length==0
@noBandsFound.removeClass("hidden")
else
@noBandsFound.addClass("hidden")
# client#/jamtrack
for artist in artists
artistLink = "<a href='client?artist=#{encodeURIComponent(artist.original_artist)}#/jamtrack/search' class='artist-link' artist='#{artist.original_artist}'>#{artist.original_artist} (#{artist.song_count})</a>"
@bandList.append("<li>#{artistLink}</li>")
# We don't want to do a full page load if this is clicked on here:
bindArtistLinks:() =>
that=this
@bandList.on "click", "a.artist-link", (event)->
context.location="client#/jamtrack/search"
if window.history.replaceState # ie9 proofing
window.history.replaceState({}, "", this.href)
event.preventDefault()
handleFailure:(error) =>

View File

@ -1,670 +0,0 @@
(function(context,$) {
"use strict";
context.JK = context.JK || {};
context.JK.OrderScreen = function(app) {
var EVENTS = context.JK.EVENTS;
var logger = context.JK.logger;
var $screen = null;
var $templateOrderContent = null;
var $templatePurchasedJamTrack = null;
var $navigation = null;
var $billingInfo = null;
var $shippingInfo = null;
var $paymentMethod = null;
var $shippingAddress = null;
var $shippingAsBilling = null;
var $paymentInfoPanel = null;
var $orderPanel = null;
var $thanksPanel = null;
var $jamTrackInBrowser = null;
var $purchasedJamTrack = null;
var $purchasedJamTrackHeader = null;
var $purchasedJamTracks = null;
var $orderContent = null;
var userDetail = null;
var step = null;
var billing_info = null;
var shipping_info = null;
var shipping_as_billing = null;
var downloadJamTracks = [];
var purchasedJamTracks = null;
var purchasedJamTrackIterator = 0;
function beforeShow() {
beforeShowPaymentInfo();
resetJamTrackDownloadInfo();
}
function beforeShowPaymentInfo() {
step = 2;
renderNavigation();
renderAccountInfo();
$("#order_error").addClass("hidden")
}
function resetJamTrackDownloadInfo() {
$purchasedJamTrack.addClass('hidden');
$purchasedJamTracks.children().remove()
$jamTrackInBrowser.hide('hidden');
}
function renderAccountInfo() {
rest.getUserDetail()
.done(populateAccountInfo)
.error(app.ajaxError);
}
function populateAccountInfo(user) {
userDetail = user;
if (userDetail.has_recurly_account) {
rest.getBillingInfo()
.done(function(response) {
$billingInfo.find("#billing-first-name").val(response.first_name);
$billingInfo.find("#billing-last-name").val(response.last_name);
$billingInfo.find("#billing-address1").val(response.address1);
$billingInfo.find("#billing-address2").val(response.address2);
$billingInfo.find("#billing-city").val(response.city);
$billingInfo.find("#billing-state").val(response.state);
$billingInfo.find("#billing-zip").val(response.zip);
$billingInfo.find("#billing-country").val(response.country);
$shippingAddress.find("#shipping-first-name").val(response.first_name);
$shippingAddress.find("#shipping-last-name").val(response.last_name);
$shippingAddress.find("#shipping-address1").val(response.address1);
$shippingAddress.find("#shipping-address2").val(response.address2);
$shippingAddress.find("#shipping-city").val(response.city);
$shippingAddress.find("#shipping-state").val(response.state);
$shippingAddress.find("#shipping-zip").val(response.zip);
$shippingAddress.find("#shipping-country").val(response.country);
})
.error(app.ajaxError);
}
else {
$billingInfo.find("#billing-first-name").val(userDetail.first_name);
$billingInfo.find("#billing-last-name").val(userDetail.last_name);
$billingInfo.find("#billing-city").val(userDetail.city);
$billingInfo.find("#billing-state").val(userDetail.state);
$billingInfo.find("#billing-country").val(userDetail.country);
$shippingAddress.find("#shipping-first-name").val(userDetail.first_name);
$shippingAddress.find("#shipping-last-name").val(userDetail.last_name);
$shippingAddress.find("#shipping-city").val(userDetail.city);
$shippingAddress.find("#shipping-state").val(userDetail.state);
$shippingAddress.find("#shipping-country").val(userDetail.country);
}
}
function afterShow(data) {
// XXX : style-test code
// moveToThanks({jam_tracks: [{id: 14, jam_track_right_id: 11, name: 'Back in Black'}, {id: 15, jam_track_right_id: 11, name: 'In Bloom'}, {id: 16, jam_track_right_id: 11, name: 'Love Bird Supreme'}]});
}
function beforeHide() {
if(downloadJamTracks) {
context._.each(downloadJamTracks, function(downloadJamTrack) {
downloadJamTrack.destroy();
downloadJamTrack.root.remove();
})
downloadJamTracks = [];
}
purchasedJamTracks = null;
purchasedJamTrackIterator = 0;
}
// TODO: Refactor: this function is long and fraught with many return points.
function next(e) {
e.preventDefault();
$("#order_error").addClass("hidden")
// validation
var billing_first_name = $billingInfo.find("#billing-first-name").val();
var billing_last_name = $billingInfo.find("#billing-last-name").val();
var billing_address1 = $billingInfo.find("#billing-address1").val();
var billing_address2 = $billingInfo.find("#billing-address2").val();
var billing_city = $billingInfo.find("#billing-city").val();
var billing_state = $billingInfo.find("#billing-state").val();
var billing_zip = $billingInfo.find("#billing-zip").val();
var billing_country = $billingInfo.find("#billing-country").val();
if (!billing_first_name) {
$billingInfo.find('#divBillingFirstName .error-text').remove();
$billingInfo.find('#divBillingFirstName').addClass("error");
$billingInfo.find('#billing-first-name').after("<ul class='error-text'><li>First Name is required</li></ul>");
return false;
}
else {
$billingInfo.find('#divBillingFirstName').removeClass("error");
}
if (!billing_last_name) {
$billingInfo.find('#divBillingLastName .error-text').remove();
$billingInfo.find('#divBillingLastName').addClass("error");
$billingInfo.find('#billing-last-name').after("<ul class='error-text'><li>Last Name is required</li></ul>");
return false;
}
else {
$billingInfo.find('#divBillingLastName').removeClass("error");
}
if (!billing_address1) {
$billingInfo.find('#divBillingAddress1 .error-text').remove();
$billingInfo.find('#divBillingAddress1').addClass("error");
$billingInfo.find('#billing-address1').after("<ul class='error-text'><li>Address is required</li></ul>");
return false;
}
else {
$billingInfo.find('#divBillingAddress1').removeClass("error");
}
if (!billing_zip) {
$billingInfo.find('#divBillingZip .error-text').remove();
$billingInfo.find('#divBillingZip').addClass("error");
$billingInfo.find('#billing-zip').after("<ul class='error-text'><li>Zip code is required</li></ul>");
return false;
}
else {
$billingInfo.find('#divBillingZip').removeClass("error");
}
if (!billing_state) {
$billingInfo.find('#divBillingState .error-text').remove();
$billingInfo.find('#divBillingState').addClass("error");
$billingInfo.find('#billing-zip').after("<ul class='error-text'><li>State is required</li></ul>");
return false;
}
else {
$billingInfo.find('#divBillingState').removeClass("error");
}
if (!billing_city) {
$billingInfo.find('#divBillingCity .error-text').remove();
$billingInfo.find('#divBillingCity').addClass("error");
$billingInfo.find('#billing-city').after("<ul class='error-text'><li>City is required</li></ul>");
return false;
}
else {
$billingInfo.find('#divBillingCity').removeClass("error");
}
if (!billing_country) {
$billingInfo.find('#divBillingCountry .error-text').remove();
$billingInfo.find('#divBillingCountry').addClass("error");
$billingInfo.find('#billing-country').after("<ul class='error-text'><li>Country is required</li></ul>");
return false;
}
else {
$billingInfo.find('#divBillingCountry').removeClass("error");
}
shipping_as_billing = $shippingAsBilling.is(":checked");
var shipping_first_name, shipping_last_name, shipping_address1, shipping_address2;
var shipping_city, shipping_state, shipping_zip, shipping_country;
if (!shipping_as_billing) {
shipping_first_name = $shippingAddress.find("#shipping-first-name").val();
shipping_last_name = $shippingAddress.find("#shipping-last-name").val();
shipping_address1 = $shippingAddress.find("#shipping-address1").val();
shipping_address2 = $shippingAddress.find("#shipping-address2").val();
shipping_city = $shippingAddress.find("#shipping-city").val();
shipping_state = $shippingAddress.find("#shipping-state").val();
shipping_zip = $shippingAddress.find("#shipping-zip").val();
shipping_country = $shippingAddress.find("#shipping-country").val();
if (!shipping_first_name) {
$shippingAddress.find('#divShippingFirstName .error-text').remove();
$shippingAddress.find('#divShippingFirstName').addClass("error");
$shippingAddress.find('#shipping-first-name').after("<ul class='error-text'><li>First Name is required</li></ul>");
return false;
}
else {
$shippingInfo.find('#divShippingFirstName').removeClass("error");
}
if (!shipping_last_name) {
$shippingAddress.find('#divShippingLastName .error-text').remove();
$shippingAddress.find('#divShippingLastName').addClass("error");
$shippingAddress.find('#shipping-last-name').after("<ul class='error-text'><li>Last Name is required</li></ul>");
return false;
}
else {
$shippingInfo.find('#divShippingLastName').removeClass("error");
}
if (!shipping_address1) {
$shippingAddress.find('#divShippingAddress1 .error-text').remove();
$shippingAddress.find('#divShippingAddress1').addClass("error");
$shippingAddress.find('#shipping-address1').after("<ul class='error-text'><li>Address is required</li></ul>");
return false;
}
else {
$shippingInfo.find('#divShippingAddress1').removeClass("error");
}
if (!shipping_zip) {
$shippingAddress.find('#divShippingZip .error-text').remove();
$shippingAddress.find('#divShippingZip').addClass("error");
$shippingAddress.find('#shipping-zip').after("<ul class='error-text'><li>Zip code is required</li></ul>");
return false;
}
else {
$shippingInfo.find('#divShippingZip').removeClass("error");
}
if (!shipping_state) {
$shippingAddress.find('#divShippingState .error-text').remove();
$shippingAddress.find('#divShippingState').addClass("error");
$shippingAddress.find('#shipping-zip').after("<ul class='error-text'><li>State is required</li></ul>");
return false;
}
else {
$shippingInfo.find('#divShippingState').removeClass("error");
}
if (!shipping_city) {
$shippingAddress.find('#divShippingCity .error-text').remove();
$shippingAddress.find('#divShippingCity').addClass("error");
$shippingAddress.find('#shipping-city').after("<ul class='error-text'><li>City is required</li></ul>");
return false;
}
else {
$shippingInfo.find('#divShippingCity').removeClass("error");
}
if (!shipping_country) {
$shippingAddress.find('#divShippingCountry .error-text').remove();
$shippingAddress.find('#divShippingCountry').addClass("error");
$shippingAddress.find('#shipping-country').after("<ul class='error-text'><li>Country is required</li></ul>");
return false;
}
else {
$shippingAddress.find('#divShippingCountry').removeClass("error");
}
}
var card_name = $paymentMethod.find("#card-name").val();
var card_number = $paymentMethod.find("#card-number").val();
var card_year = $paymentMethod.find("#card_expire-date_1i").val();
var card_month = $paymentMethod.find("#card_expire-date_2i").val();
var card_verify = $paymentMethod.find("#card-verify").val();
if (!card_name) {
$paymentMethod.find('#divCardName .error-text').remove();
$paymentMethod.find('#divCardName').addClass("error");
$paymentMethod.find('#card-name').after("<ul class='error-text'><li>Card Name is required</li></ul>");
return false;
} else {
$paymentMethod.find('#divCardName').removeClass("error");
}
if (!card_number) {
$paymentMethod.find('#divCardNumber .error-text').remove();
$paymentMethod.find('#divCardNumber').addClass("error");
$paymentMethod.find('#card-number').after("<ul class='error-text'><li>Card Number is required</li></ul>");
return false;
} else if (!$.payment.validateCardNumber(card_number)) {
$paymentMethod.find('#divCardNumber .error-text').remove();
$paymentMethod.find('#divCardNumber').addClass("error");
$paymentMethod.find('#card-number').after("<ul class='error-text'><li>Card Number is not valid</li></ul>");
return false;
} else {
$paymentMethod.find('#divCardNumber').removeClass("error");
}
if (!$.payment.validateCardExpiry(card_month, card_year)) {
$paymentMethod.find('#divCardExpiry .error-text').remove();
$paymentMethod.find('#divCardExpiry').addClass("error");
$paymentMethod.find('#card-expiry').after("<ul class='error-text'><li>Card Number is not valid</li></ul>");
} else {
$paymentMethod.find('#divCardExpiry').removeClass("error");
}
if (!card_verify) {
$paymentMethod.find('#divCardVerify .error-text').remove();
$paymentMethod.find('#divCardVerify').addClass("error");
$paymentMethod.find('#card-verify').after("<ul class='error-text'><li>Card Verification Value is required</li></ul>");
return false;
} else if(!$.payment.validateCardCVC(card_verify)) {
$paymentMethod.find('#divCardVerify .error-text').remove();
$paymentMethod.find('#divCardVerify').addClass("error");
$paymentMethod.find('#card-verify').after("<ul class='error-text'><li>Card Verification Value is not valid.</li></ul>");
return false;
} else {
$paymentMethod.find('#divCardVerify').removeClass("error");
}
billing_info = {};
shipping_info = {};
billing_info.first_name = billing_first_name;
billing_info.last_name = billing_last_name;
billing_info.address1 = billing_address1;
billing_info.address2 = billing_address2;
billing_info.city = billing_city;
billing_info.state = billing_state;
billing_info.country = billing_country;
billing_info.zip = billing_zip;
billing_info.number = card_number;
billing_info.month = card_month;
billing_info.year = card_year;
billing_info.verification_value = card_verify;
if (shipping_as_billing) {
shipping_info = $.extend({},billing_info);
delete shipping_info.number;
delete shipping_info.month;
delete shipping_info.year;
delete shipping_info.verification_value;
} else {
shipping_info.first_name = shipping_first_name;
shipping_info.last_name = shipping_last_name;
shipping_info.address1 = shipping_address1;
shipping_info.address2 = shipping_address2;
shipping_info.city = shipping_city;
shipping_info.state = shipping_state;
shipping_info.country = shipping_country;
shipping_info.zip = shipping_zip;
}
$paymentInfoPanel.find("#payment-info-next").addClass("disabled");
$paymentInfoPanel.find("#payment-info-next").off("click");
rest.createRecurlyAccount({billing_info: billing_info})
.done(function() {
moveToOrder();
$paymentInfoPanel.find("#payment-info-next").removeClass("disabled");
$paymentInfoPanel.find("#payment-info-next").on("click", next);
})
.fail(errorHandling);
}
function errorHandling(xhr, ajaxOptions, thrownError) {
$.each(xhr.responseJSON.errors, function(key, error) {
if (key == 'number') {
$paymentMethod.find('#divCardNumber .error-text').remove();
$paymentMethod.find('#divCardNumber').addClass("error");
$paymentMethod.find('#card-number').after("<ul class='error-text'><li>" + error + "</li></ul>");
}
else if (key == 'verification_value') {
$paymentMethod.find('#divCardVerify .error-text').remove();
$paymentMethod.find('#divCardVerify').addClass("error");
$paymentMethod.find('#card-verify').after("<ul class='error-text'><li>" + error + "</li></ul>");
}
});
$paymentInfoPanel.find("#payment-info-next").addClass("disabled");
$paymentInfoPanel.find("#payment-info-next").on('click', next);
}
function orderErrorHandling(xhr, ajaxOptions, thrownError) {
var message = "Error submitting payment: "
$.each(xhr.responseJSON.errors, function(key, error) {
message += key + ": " + error
})
$("#order_error").text(message)
$("#order_error").removeClass("hidden")
$orderContent.find(".place-order").on('click', placeOrder)
}
function beforeShowOrder() {
step = 3;
renderNavigation();
populateOrderPage();
}
function clearOrderPage() {
$orderContent.empty();
}
function populateOrderPage() {
clearOrderPage();
rest.getShoppingCarts()
.done(renderOrderPage)
.fail(app.ajaxError);
}
function renderOrderPage(carts) {
var data = {}
var sub_total = 0.0
var taxes = 0.0
$.each(carts, function(index, cart) {
sub_total += parseFloat(cart.product_info.price) * parseFloat(cart.quantity)
});
data.grand_total = (sub_total + taxes).toFixed(2)
data.sub_total = sub_total.toFixed(2)
data.taxes = taxes.toFixed(2)
data.carts = carts
data.billing_info = billing_info
data.shipping_info = shipping_info
data.shipping_as_billing = shipping_as_billing
var orderContentHtml = $(
context._.template(
$templateOrderContent.html(),
data,
{variable: 'data'}
)
)
$orderContent.append(orderContentHtml)
$orderPanel.find(".change-payment-info").on('click', moveToPaymentInfo)
$orderContent.find(".place-order").on('click', placeOrder)
}
function moveToOrder() {
$paymentInfoPanel.addClass("hidden");
$orderPanel.removeClass("hidden");
beforeShowOrder();
}
function moveToThanks(purchaseResponse) {
$("#order_error").addClass("hidden")
$paymentInfoPanel.addClass("hidden")
$orderPanel.addClass("hidden")
$thanksPanel.removeClass("hidden")
rest.clearShoppingCart()
beforeShowOrder()
handleJamTracksPurchased(purchaseResponse.jam_tracks)
}
function handleJamTracksPurchased(jamTracks) {
// were any JamTracks purchased?
var jamTracksPurchased = jamTracks && jamTracks.length > 0;
if(jamTracksPurchased) {
if(gon.isNativeClient) {
startDownloadJamTracks(jamTracks)
}
else {
$jamTrackInBrowser.removeClass('hidden');
}
}
}
function startDownloadJamTracks(jamTracks) {
// there can be multiple purchased JamTracks, so we cycle through them
purchasedJamTracks = jamTracks;
// populate list of jamtracks purchased, that we will iterate through graphically
context._.each(jamTracks, function(jamTrack) {
var downloadJamTrack = new context.JK.DownloadJamTrack(app, jamTrack, 'small');
var $purchasedJamTrack = $(context._.template(
$templatePurchasedJamTrack.html(),
jamTrack,
{variable: 'data'}
));
$purchasedJamTracks.append($purchasedJamTrack)
// show it on the page
$purchasedJamTrack.append(downloadJamTrack.root)
downloadJamTracks.push(downloadJamTrack)
})
iteratePurchasedJamTracks();
}
function iteratePurchasedJamTracks() {
if(purchasedJamTrackIterator < purchasedJamTracks.length ) {
var downloadJamTrack = downloadJamTracks[purchasedJamTrackIterator++];
// make sure the 'purchasing JamTrack' section can be seen
$purchasedJamTrack.removeClass('hidden');
// the widget indicates when it gets to any transition; we can hide it once it reaches completion
$(downloadJamTrack).on(EVENTS.JAMTRACK_DOWNLOADER_STATE_CHANGED, function(e, data) {
if(data.state == downloadJamTrack.states.synchronized) {
logger.debug("jamtrack " + downloadJamTrack.jamTrack.name + " synchronized;")
//downloadJamTrack.root.remove();
downloadJamTrack.destroy();
// go to the next JamTrack
iteratePurchasedJamTracks()
}
})
logger.debug("jamtrack " + downloadJamTrack.jamTrack.name + " downloader initializing")
// kick off the download JamTrack process
downloadJamTrack.init()
// XXX style-test code
// downloadJamTrack.transitionError("package-error", "The server failed to create your package.")
}
else {
logger.debug("done iterating over purchased JamTracks")
$purchasedJamTrackHeader.text('All purchased JamTracks have been downloaded successfully! You can now play them in a session.')
}
}
function moveToPaymentInfo(e) {
e.preventDefault();
$paymentInfoPanel.removeClass("hidden");
$orderPanel.addClass("hidden");
beforeShowPaymentInfo();
}
function toggleShippingAsBilling(e) {
e.preventDefault();
var shipping_as_billing = $(e.target).is(':checked');
if (!shipping_as_billing) {
$shippingAddress.removeClass("hidden");
}
else {
$shippingAddress.addClass("hidden");
}
}
function placeOrder(e) {
e.preventDefault();
$orderContent.find(".place-order").off('click')
rest.getShoppingCarts()
.done(function(carts) {
var jam_track_ids = _.map(carts, function(cart){
return cart.product_info.product_id
})
rest.placeOrder({jam_tracks: jam_track_ids})
.done(moveToThanks)
.fail(orderErrorHandling);
}
).fail(app.ajaxError);
}
function events() {
$paymentInfoPanel.find("#payment-info-next").on('click', next);
$shippingAsBilling.on('ifChanged', toggleShippingAsBilling);
}
function reset() {
}
function renderNavigation() {
$navigation.html("");
var navigationHtml = $(
context._.template(
$('#template-checkout-navigation').html(),
{current: step},
{variable: 'data'}
)
);
$navigation.append(navigationHtml);
}
function initializeControls() {
$("form.payment-info").iCheck({
checkboxClass: 'icheckbox_minimal',
radioClass: 'iradio_minimal',
inheritClass: true
});
// Use jquery.payment to limit characters and length:
$paymentMethod.find("#card-number").payment('formatCardNumber');
$paymentMethod.find("#card-verify").payment('formatCardCVC');
}
function initialize() {
var screenBindings = {
'beforeShow': beforeShow,
'afterShow': afterShow,
'beforeHide' : beforeHide
};
app.bindScreen('order', screenBindings);
$screen = $("#orderScreen");
$templateOrderContent = $("#template-order-content");
$templatePurchasedJamTrack = $('#template-purchased-jam-track');
$paymentInfoPanel = $screen.find(".checkout-payment-info");
$orderPanel = $screen.find(".order-panel");
$thanksPanel = $screen.find(".thanks-panel");
$jamTrackInBrowser = $screen.find(".thanks-detail.jam-tracks-in-browser");
$purchasedJamTrack = $thanksPanel.find(".thanks-detail.purchased-jam-track");
$purchasedJamTrackHeader = $purchasedJamTrack.find(".purchased-jam-track-header");
$purchasedJamTracks = $purchasedJamTrack.find(".purchased-list")
$navigation = $screen.find(".checkout-navigation-bar");
$billingInfo = $paymentInfoPanel.find(".billing-address");
$shippingInfo = $paymentInfoPanel.find(".shipping-address");
$paymentMethod = $paymentInfoPanel.find(".payment-method");
$shippingAddress = $paymentInfoPanel.find(".shipping-address-detail");
$shippingAsBilling = $paymentInfoPanel.find("#shipping-as-billing");
$orderContent = $orderPanel.find(".order-content");
if($screen.length == 0) throw "$screen must be specified";
if($navigation.length == 0) throw "$navigation must be specified";
initializeControls();
events();
}
this.initialize = initialize;
return this;
}
})(window,jQuery);

View File

@ -4,17 +4,11 @@ MIX_MODES = context.JK.MIX_MODES
@JamTrackFilterScreen = React.createClass({
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
mixins: [Reflux.listenTo(@AppStore,"onAppInit"), Reflux.listenTo(@UserStore,"onUserChanged")]
LIMIT: 20
instrument_logo_map: context.JK.getInstrumentIconMap24()
computeWeight: (jam_track_track, instrument) ->
weight = switch
when jam_track_track.track_type == 'Master' then 0
when jam_track_track.instrument?.id == instrument then 1 + jam_track_track.position
else 10000 + jam_track_track.position
render: () ->
searchText = if @state.first_search then 'SEARCH' else 'SEARCH AGAIN'
@ -76,10 +70,10 @@ MIX_MODES = context.JK.MIX_MODES
</div>`
actionBtn = null
if jamtrack.is_free
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id}> GET IT FREE!</a>`
else if jamtrack.purchased
if jamtrack.purchased
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="javascript:void(0)">PURCHASED</a>`
else if jamtrack.is_free
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id}> GET IT FREE!</a>`
else if jamtrack.added_cart
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="client#/shoppingCart">ALREADY IN CART</a>`
else
@ -158,15 +152,13 @@ MIX_MODES = context.JK.MIX_MODES
</div>
</div>`
getInitialState: () ->
{search: '', type: 'user-input', jamtracks:[], show_all_artists: false, currentPage: 0, next: null, searching: false, count: 0, is_free: context.JK.currentUserFreeJamTrack}
clearResults:() ->
#@content.empty()
#@noMoreJamtracks.hide()
@setState({currentPage: 0, next: null, jamtracks:[], type: 'user-input', searching:false, is_free: context.JK.currentUserFreeJamTrack})
@setState({currentPage: 0, next: null, jamtracks:[], type: 'user-input', searching:false, is_free: @user.show_free_jamtrack})
getInitialState: () ->
{search: '', type: 'user-input', jamtracks:[], show_all_artists: false, currentPage: 0, next: null, searching: false, count: 0, is_free: context.JK.currentUserFreeJamTrack}
defaultQuery:(extra) ->
@ -305,16 +297,21 @@ MIX_MODES = context.JK.MIX_MODES
isFree = $(e.target).is('.is_free')
@rest.addJamtrackToShoppingCart(params).done((response) =>
if(isFree)
if context.JK.currentUserId?
context.JK.currentUserFreeJamTrack = true # make sure the user sees no more free notices
context.location = '/client#/redeemComplete'
if context.JK.currentUserId?
if isFree
if @user.has_redeemable_jamtrack
# this is the 1st jamtrack; let's user the user to completion
context.location = '/client#/redeemComplete'
else
# this is must be a user's gifted jamtrack, to treat them normally in that they'll go to the shopping cart
#context.location = '/client#/shoppingCart'
context.location = '/client#/redeemComplete'
else
# now make a rest call to buy it
context.location = '/client#/redeemSignup'
# this user has nothing free; so send them to shopping cart
context.location = '/client#/shoppingCart'
else
context.location = '/client#/shoppingCart'
# user not logged in; make them signup
context.location = '/client#/redeemSignup'
).fail(() => @app.ajaxError)
@ -398,4 +395,15 @@ MIX_MODES = context.JK.MIX_MODES
'afterShow': @afterShow
@app.bindScreen('jamtrack/filter', screenBindings)
onUserChanged: (userState) ->
@user = userState?.user
@setState({is_free: @user?.show_free_jamtrack})
computeWeight: (jam_track_track, instrument) ->
weight = switch
when jam_track_track.track_type == 'Master' then 0
when jam_track_track.instrument?.id == instrument then 1 + jam_track_track.position
else 10000 + jam_track_track.position
})

View File

@ -13,12 +13,13 @@ rest = context.JK.Rest()
{user: null, purchasedJamTracks: []}
onUserChanged: (userState) ->
@user = userState?.user
@onUser(userState.user) if userState.user
render: () ->
howTo = null
if @state.user?.free_jamtrack
if @user?.purchased_jamtracks_count == 0 && @user?.has_redeemable_jamtrack
howTo =
`<div className="free-jamtrack">
<span>
@ -206,18 +207,14 @@ rest = context.JK.Rest()
@processUrl()
if !context.JK.currentUserId
@onUser({free_jamtrack: context.JK.currentUserFreeJamTrack})
beforeShow: () ->
@setState({user: null})
onUser:(user) ->
@setState({user: user})
@doPurchasedSearch()
if context.JK.currentUserId?
@doPurchasedSearch()
# Get artist names and build links
#@rest.getJamTrackArtists({group_artist: true, per_page:100})

View File

@ -4,7 +4,7 @@ MIX_MODES = context.JK.MIX_MODES
@JamTrackSearchScreen = React.createClass({
mixins: [Reflux.listenTo(@AppStore,"onAppInit")]
mixins: [Reflux.listenTo(@AppStore,"onAppInit"), Reflux.listenTo(@UserStore,"onUserChanged")]
LIMIT: 10
instrument_logo_map: context.JK.getInstrumentIconMap24()
@ -91,10 +91,10 @@ MIX_MODES = context.JK.MIX_MODES
</div>`
actionBtn = null
if jamtrack.is_free
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id}> GET IT FREE!</a>`
else if jamtrack.purchased
if jamtrack.purchased
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="javascript:void(0)">PURCHASED</a>`
else if jamtrack.is_free
actionBtn = `<a className="jamtrack-add-cart button-orange is_free" href="#" data-jamtrack-id={jamtrack.id}> GET IT FREE!</a>`
else if jamtrack.added_cart
actionBtn = `<a className="jamtrack-add-cart-disabled button-grey button-disabled" href="client#/shoppingCart">ALREADY IN CART</a>`
else
@ -219,14 +219,13 @@ MIX_MODES = context.JK.MIX_MODES
clearResults:() ->
@setState({currentPage: 0, next: null, show_all_artists: false, artists:[], jamtracks:[], type: 'user-input', searching:false, artist: null, song:null, is_free: context.JK.currentUserFreeJamTrack, first_search: true})
@setState({currentPage: 0, next: null, show_all_artists: false, artists:[], jamtracks:[], type: 'user-input', searching:false, artist: null, song:null, is_free: @user.show_free_jamtrack, first_search: true})
getInitialState: () ->
{search: '', type: 'user-input', artists:[], jamtracks:[], show_all_artists: false, currentPage: 0, next: null, searching: false, first_search: true, count: 0, is_free: context.JK.currentUserFreeJamTrack}
onSelectChange: (val) ->
#@logger.debug("CHANGE #{val}")
return false unless val?
@ -435,18 +434,39 @@ MIX_MODES = context.JK.MIX_MODES
isFree = $(e.target).is('.is_free')
@rest.addJamtrackToShoppingCart(params).done((response) =>
if(isFree)
if context.JK.currentUserId?
context.JK.currentUserFreeJamTrack = true # make sure the user sees no more free notices
context.location = '/client#/redeemComplete'
if context.JK.currentUserId?
if isFree
if @user.has_redeemable_jamtrack
# this is the 1st jamtrack; let's user the user to completion
context.location = '/client#/redeemComplete'
else
# this is must be a user's gifted jamtrack, to treat them normally in that they'll go to the shopping cart
#context.location = '/client#/shoppingCart'
context.location = '/client#/redeemComplete'
else
# now make a rest call to buy it
context.location = '/client#/redeemSignup'
# this user has nothing free; so send them to shopping cart
context.location = '/client#/shoppingCart'
else
context.location = '/client#/shoppingCart'
if isFree
# user not logged in; make them signup
context.location = '/client#/redeemSignup'
else
# this user has nothing free; so send them to shopping cart
context.location = '/client#/shoppingCart'
).fail(() => @app.ajaxError)
).fail(((jqxhr) =>
handled = false
if jqxhr.status == 422
body = JSON.parse(jqxhr.responseText)
if body.errors && body.errors.base
handled = true
context.JK.Banner.showAlert("You can not have a mix of free and non-free items in your shopping cart.<br/><br/>If you want to add this new item to your shopping cart, then clear out all current items by clicking on the shopping cart icon and clicking 'delete' next to each item.")
if !handled
@app.ajaxError(arguments[0], arguments[1], arguments[2])
))
licenseUSWhy:(e) ->
e.preventDefault()
@ -517,12 +537,9 @@ MIX_MODES = context.JK.MIX_MODES
beforeShow: () ->
@setState({is_free: context.JK.currentUserFreeJamTrack})
if !@state.first_search
@search(@state.type, window.JamTrackSearchInput)
onAppInit: (@app) ->
window.JamTrackSearchInput = '' # need to be not null; otherwise react-select chokes
@ -530,10 +547,14 @@ MIX_MODES = context.JK.MIX_MODES
@rest = context.JK.Rest()
@logger = context.JK.logger
screenBindings =
'beforeShow': @beforeShow
'afterShow': @afterShow
@app.bindScreen('jamtrack/search', screenBindings)
onUserChanged: (userState) ->
@user = userState?.user
@setState({is_free: @user?.show_free_jamtrack})
})

View File

@ -494,7 +494,7 @@ mixins.push(Reflux.listenTo(JamTrackPlayerStore, 'onJamTrackPlayerStoreChanged')
e.preventDefault()
iframe = document.createElement("iframe")
iframe.src = @createStemUrl(@state.jamTrackState.jamTrack.id, stemId)
iframe.src = @createStemUrl(@state.jamTrackState.jamTrack.id, selectedTrackId)
iframe.style.display = "none"
document.body.appendChild(iframe);
@ -728,6 +728,7 @@ mixins.push(Reflux.listenTo(JamTrackPlayerStore, 'onJamTrackPlayerStoreChanged')
setTimeout(@resizeWindow, 1000)
resizeWindow: () =>
return
$container = $('#minimal-container')
width = $container.width()
height = $container.height()

View File

@ -2,5 +2,6 @@ context = window
@UserActions = Reflux.createActions({
loaded: {}
modify: {}
})

View File

@ -119,10 +119,9 @@ rest = context.JK.Rest()
isFree = context.JK.currentUserFreeJamTrack
rest.addJamtrackToShoppingCart({id: @props.jam_track.id}).done((response) =>
rest.addJamtrackToShoppingCart({id: @props.jam_track.id, clear:true}).done((response) =>
if isFree
if loggedIn
context.JK.currentUserFreeJamTrack = true # make sure the user sees no more free notices
@setState({done: true})
context.location = '/client?redeemed_flow=1#/jamtrack'
else

View File

@ -0,0 +1,159 @@
context = window
rest = context.JK.Rest()
ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
@RedeemGiftCardPage = React.createClass({
render: () ->
if this.state.formErrors?
for key, value of this.state.formErrors
break
errorText = context.JK.getFullFirstError(key, @state.formErrors, {email: 'Email', password: 'Password', gift_card: 'Gift Card Code', 'terms_of_service' : 'The terms of service'})
buttonClassnames = classNames({'redeem-giftcard': true, 'button-orange': true, disabled: @state.processing || @state.done })
if @state.done
button =
`<div key="done" className="done-action">
<div>You have {this.state.gifted_jamtracks} free JamTracks on your account!</div>
<div>You can now <a className="go-browse" href="/client#/jamtrack">browse our collection</a> and redeem them.</div>
</div>`
else
button = `<button key="button" className={buttonClassnames} onClick={this.action}>REDEEM GIFT CARD</button>`
action = `<ReactCSSTransitionGroup transitionName="session-track-list">
{button}
</ReactCSSTransitionGroup>`
if context.JK.currentUserId?
form =
`<form onSubmit={this.submit}>
<label>Gift Card Code:</label><input type="text" name="code"/>
{action}
</form>`
instruments = `<p className="instructions">Enter the code from the back of your gift card to associate it with your account.</p>`
else
form =
`<form onSubmit={this.submit}>
<label>Gift Card Code:</label><input type="text" name="code"/>
<label>Email:</label><input type="text" name="email"/>
<label>Password:</label><input type="password" name="password"/>
<div className="clearall"/>
<input className="terms-checkbox" type="checkbox" name="terms" /><label className="terms-help">I have read and agree to the JamKazam <a href="/corp/terms" onClick={this.termsClicked}>terms of service</a></label>
<div className="clearall"/>
{action}
</form>`
instruments = `<p className="instructions">Enter the code from the back of your gift card to associate it with your new JamKazam account.</p>`
classes = classNames({'redeem-container': true, 'not-logged-in': !context.JK.currentUserId?, 'logged-in': context.JK.currentUserId? })
`<div className={classes}>
<div className="redeem-content">
<h2>Redeem Your Gift Card</h2>
{instruments}
{form}
<div className={classNames({'errors': true, 'active': this.state.formErrors})}>
{errorText}
</div>
</div>
</div>`
getInitialState: () ->
{formErrors: null, processing:false, gifted_jamtracks: null}
privacyPolicy: (e) ->
e.preventDefault()
context.JK.popExternalLink('/corp/privacy')
termsClicked: (e) ->
e.preventDefault()
context.JK.popExternalLink('/corp/terms')
componentDidMount:() ->
$root = $(this.getDOMNode())
$checkbox = $root.find('.terms-checkbox')
console.log("$checkbox", $checkbox)
context.JK.checkbox($checkbox)
submit: (e) ->
@action(e)
action: (e) ->
if @state.done || @state.processing
e.preventDefault()
return
if context.JK.currentUserId?
@redeem(e)
else
@signup(e)
redeem: (e) ->
e.preventDefault()
return if @state.done || @state.processing
$root = $(@getDOMNode())
$code = $root.find('input[name="code"]')
code = $code.val()
rest.redeemGiftCard({gift_card: code})
.done((response) =>
@setState({formErrors: null, processing:false, done: true, gifted_jamtracks: response.gifted_jamtracks})
).fail((jqXHR) =>
@setState({processing:false})
if jqXHR.status == 422
response = JSON.parse(jqXHR.responseText)
if response.errors
@setState({formErrors: response.errors})
else
context.JK.app.notify({title: 'Unknown Error', text: jqXHR.responseText})
else
context.JK.app.notifyServerError(jqXHR, "Unable to Redeem Giftcard")
)
signup: (e) ->
e.preventDefault()
return if @state.done || @state.processing
$root = $(@getDOMNode())
$email = $root.find('input[name="email"]')
$code = $root.find('input[name="code"]')
$password = $root.find('input[name="password"]')
terms = $root.find('input[name="terms"]').is(':checked')
@setState({processing:true})
email = $email.val()
password = $password.val()
code = $code.val()
if !code
# must pass up non-null value to indicate user is trying to redeem giftcard while creating account
code = ''
rest.signup({email: email, password: password, gift_card: code, terms: terms})
.done((response) =>
@setState({formErrors: null, processing:false, done: true, gifted_jamtracks: response.gifted_jamtracks})
).fail((jqXHR) =>
@setState({processing:false})
if jqXHR.status == 422
response = JSON.parse(jqXHR.responseText)
if response.errors
@setState({formErrors: response.errors})
else
context.JK.app.notify({title: 'Unknown Signup Error', text: jqXHR.responseText})
else
context.JK.app.notifyServerError(jqXHR, "Unable to Sign Up")
)
})

View File

@ -8,10 +8,24 @@ logger = context.JK.logger
listenables: @UserActions
init: ->
this.listenTo(context.AppStore, this.onAppInit)
onAppInit: (@app) ->
@loadAnonymousUser()
loadAnonymousUser: () ->
@user = {id: null, has_redeemable_jamtrack: context.JK.currentUserFreeJamTrack, purchased_jamtracks_count:0, show_free_jamtrack: context.JK.currentUserFreeJamTrack }
@changed()
onLoaded:(user) ->
@user = user
@changed()
onModify: (changes) ->
@user = $.extend({}, @user, changes)
@changed(
)
changed:() ->
@trigger({user: @user})
}

View File

@ -273,6 +273,7 @@
function handleRecordingStopped(recordingId, result) {
if(recordingId == "video") {
// comes from VideoRecordingStopped
return;
}
@ -324,6 +325,16 @@
function handleRecordingAborted(recordingId, result) {
if(recordingId == "video") {
/**
// comes from AbortedVideoRecording
recordingId = result;
if (arguments.length == 2) {
result = arguments[2]
}
logger.debug("video recording aborted", result)
context.JK.Banner.showAlert("Video has stopped recording. Audio is still recording...")
context.RecordingActions.stopRecording()
*/
return;
}

View File

@ -31,11 +31,6 @@
function beforeShow() {
}
function afterShow(data) {
context.JK.Tracking.redeemCompleteTrack()
$noPurchasesPrompt.addClass('hidden')
@ -44,6 +39,10 @@
$purchasedJamTrackHeader.attr('status', 'in-progress')
$jamTrackInBrowser.addClass('hidden')
$jamTrackInClient.addClass('hidden')
}
function afterShow(data) {
// if there is no current user, but it apperas we have a login cookie, just refresh
@ -59,23 +58,19 @@
function handleShoppingCartResponse(carts) {
if(!checkoutUtils.hasOneFreeItemInShoppingCart(carts)) {
if(!checkoutUtils.hasOnlyFreeItemsInShoppingCart(carts)) {
// the user has multiple items in their shopping cart. They shouldn't be here.
logger.error("invalid access of redeemComplete page")
window.location = '/client#/jamtrack/search'
}
else {
// ok, we have one, free item. save it for
shoppingCartItem = carts[0];
rest.placeOrder()
.done(function(purchaseResponse) {
context.JK.currentUserFreeJamTrack = false // make sure the user sees no more free notices without having to do a full page refresh
rest.updateUser()
checkoutUtils.setLastPurchase(purchaseResponse)
jamTrackUtils.checkShoppingCart()
//app.refreshUser() // this only causes grief in tests for some reason, and with currentUserFreeJamTrack = false above, this is probably now unnecessary
prepThanks();
})

View File

@ -73,7 +73,7 @@
else if(carts.length > 1) {
// the user has multiple items in their shopping cart. They shouldn't be here.
logger.error("invalid access of redeemJamTrack page; multiple")
window.location = '/client#/jamtrack/search'
window.location = '/client#/shoppingCart'
}
else {
var item = carts[0];

View File

@ -9,6 +9,7 @@
var $screen = null;
var $content = null;
var totalCost = 0;
function beforeShow(data) {
clearContent();
@ -30,7 +31,7 @@
function proceedCheckout(e) {
e.preventDefault();
if (context.JK.currentUserFreeJamTrack) {
if (totalCost == 0) {
if(context.JK.currentUserId) {
logger.debug("proceeding to redeem complete screen because user has a free jamtrack and is logged in")
window.location = '/client#/redeemComplete'
@ -120,6 +121,8 @@
});
data.sub_total = sub_total;
totalCost = data.sub_total;
data.carts = carts;
var $cartsHtml = $(
context._.template(

View File

@ -702,4 +702,23 @@ $ReactSelectVerticalPadding: 3px;
.Select-search-prompt {
padding:3px 0 !important;
}
}
.session-track-list-enter {
opacity: 0.01;
transition: opacity .5s ease-in;
&.session-track-list-enter-active {
opacity: 1;
}
}
.session-track-list-leave {
opacity:1;
transition: opacity .5s ease-in;
&.session-track-list-leave-active {
opacity: 0.01;
}
}

View File

@ -386,22 +386,3 @@ $session-screen-divider: 1190px;
}
}
}
.session-track-list-enter {
opacity: 0.01;
transition: opacity .5s ease-in;
&.session-track-list-enter-active {
opacity: 1;
}
}
.session-track-list-leave {
opacity:1;
transition: opacity .5s ease-in;
&.session-track-list-leave-active {
opacity: 0.01;
}
}

View File

@ -74,7 +74,7 @@
display:inline-block;
}
.download-jamkazam-wrapper {
.download-jamkazam-wrapper, .back-to-browsing {
text-align:center;
display:block;
margin-top:35px;
@ -83,6 +83,7 @@
display:none;
}
}
.thanks-detail.purchased-jam-track {
margin-top:20px;

View File

@ -0,0 +1,90 @@
@import "client/common";
body.web.redeem_giftcard {
h2 {
margin-bottom:20px;
}
label{
margin-bottom:4px;
color:$ColorTextTypical;
}
input{
margin-bottom:20px;
width:200px;
}
.redeem-container {
margin-left:350px;
width:400px;
padding-top:20px;
&.logged-in {
button {
margin-top:10px !important;
}
}
&.not-logged-in {
}
}
.redeem-content {
}
p.instructions {
line-height:125%;
color:$ColorTextTypical;
margin-bottom:20px;
}
button {
display:block !important;
height: 29px !important;
margin-bottom: 10px;
margin-right: 0px;
font-size: 16px !important;
padding: 7px 3px !important;
line-height:inherit !important;
margin-left:2px !important;
margin-top:15px;
}
.icheckbox_minimal {
float: left;
top: -2px;
margin-left: 0;
margin-right:10px;
}
.errors {
font-size:14px;
height:20px;
margin:0;
visibility: hidden;
color: red;
font-weight: bold;
&.active {
visibility: visible;
}
}
form {
margin-bottom:20px;
}
.terms-help {
float:left;
margin-top:-5px;
font-size:12px;
width:178px;
}
.done-action {
margin-top: 20px;
line-height: 125%;
}
}

View File

@ -6,12 +6,14 @@ body.jamtrack-player-popup.popup {
background-color: #242323;
overflow:auto !important;
padding-top:20px !important;
.wrapper {
width: 450px;
position: relative;
margin: 0px auto;
padding: 0 10px 10px;
border-width: 0 1px 1px;
border-width: 1px;
border-color: #ed3618;
border-style: solid;
}

View File

@ -16,7 +16,12 @@ class ApiAuthsController < ApiController
complete_sign_in(user, redirect=false)
render :json => {}, :status => :ok
render :json => {
first_name: user.first_name,
last_name: user.last_name,
photo_url: user.photo_url,
email: user.email
}, :status => :ok
end
end
end

View File

@ -3,7 +3,11 @@ class ApiGenresController < ApiController
respond_to :json
def index
@genres = Genre.order(:description)
if params[:jamtracks]
@genres = Genre.jam_track_list
else
@genres = Genre.order(:description)
end
end
def show

View File

@ -3,7 +3,11 @@ class ApiInstrumentsController < ApiController
respond_to :json
def index
@instruments = Instrument.standard_list
if params[:jamtracks]
@instruments = Instrument.jam_track_list
else
@instruments = Instrument.standard_list
end
end
def show

View File

@ -3,7 +3,7 @@ class ApiJamTracksController < ApiController
# have to be signed in currently to see this screen
before_filter :api_signed_in_user, :except => [:index, :autocomplete, :show_with_artist_info, :artist_index]
before_filter :api_any_user, :only => [:index, :autocomplete, :show_with_artist_info, :artist_index]
before_filter :lookup_jam_track_right, :only => [:download,:enqueue, :show_jam_track_right, :mark_active, :download_stem]
before_filter :lookup_jam_track_right, :only => [:download, :enqueue, :show_jam_track_right, :mark_active, :download_stem]
before_filter :ip_blacklist, :only => [:download_stem, :download]
before_filter :user_blacklist, :only => [:download_stem, :download]

View File

@ -126,8 +126,15 @@ class ApiRecurlyController < ApiController
error=nil
response = {jam_tracks: []}
if Sale.is_mixed(current_user.shopping_carts)
msg = "has free and non-free items. Try removing non-free items."
render json: {message: "Cart " + msg, errors: {cart: [msg]}}, :status => 404
return
end
sales = Sale.place_order(current_user, current_user.shopping_carts)
sales.each do |sale|
if sale.is_jam_track_sale?
sale.sale_line_items.each do |line_item|

View File

@ -1,7 +1,7 @@
class ApiSearchController < ApiController
# have to be signed in currently to see this screen
before_filter :api_signed_in_user
before_filter :api_signed_in_user, :except => :jam_tracks
respond_to :json
@ -66,4 +66,19 @@ class ApiSearchController < ApiController
end
end
def jam_tracks
if request.get?
if params[:iso639]
render(json: JamTrackSearch.all_languages.to_json, status: 200) and return
else
render(json: {}, status: 200) and return
end
elsif request.post?
jts = JamTrackSearch.new
filter = request.params[:api_search]
result = jts.search_results_page(filter)
render(json: result.to_json, status: 200) and return
end
end
end

View File

@ -20,13 +20,13 @@ class ApiShoppingCartsController < ApiController
raise StateError, "Invalid JamTrack."
end
@cart = ShoppingCart.add_jam_track_to_cart(any_user, jam_track)
@cart = ShoppingCart.add_jam_track_to_cart(any_user, jam_track, clear:params[:clear])
if @cart.errors.any?
response.status = :unprocessable_entity
respond_with @cart
else
respond_with @cart, responder: ApiResponder, :status => 201
# let add_jamtrack.rabl take over
end
end
@ -42,7 +42,7 @@ class ApiShoppingCartsController < ApiController
response.statue = :unprocessable_entity
respond_with @cart
else
respond_with @cart, responder: ApiResponder, :status => 200
# let update_cart.rabl take over
end
end
@ -52,7 +52,7 @@ class ApiShoppingCartsController < ApiController
ShoppingCart.remove_jam_track_from_cart(any_user, @cart)
respond_with responder: ApiResponder, :status => 204
# let remove_cart.rabl take over
end
# take all shopping carts from anonymous user and copy them to logged in user

View File

@ -14,8 +14,8 @@ ApiUsersController < ApiController
:band_invitation_index, :band_invitation_show, :band_invitation_update, # band invitations
:set_password, :begin_update_email, :update_avatar, :delete_avatar, :generate_filepicker_policy,
:share_session, :share_recording,
:affiliate_report, :audio_latency, :broadcast_notification]
before_filter :ip_blacklist, :only => [:create]
:affiliate_report, :audio_latency, :broadcast_notification, :redeem_giftcard]
before_filter :ip_blacklist, :only => [:create, :redeem_giftcard]
respond_to :json, :except => :calendar
respond_to :ics, :only => :calendar
@ -81,6 +81,7 @@ ApiUsersController < ApiController
terms_of_service: params[:terms],
location: {:country => nil, :state => nil, :city => nil},
signup_hint: signup_hint,
gift_card: params[:gift_card],
affiliate_referral_id: cookies[:affiliate_visitor]
}
@ -919,6 +920,45 @@ ApiUsersController < ApiController
.find(params[:id])
end
def redeem_giftcard
@gift_card = GiftCard.find_by_code(params[:gift_card])
if @gift_card.nil?
render json: {errors:{gift_card: ['does not exist']}}, status: 422
return
end
if current_user.gift_cards.count >= 5
render json: {errors:{gift_card: ['has too many on account']}}, status: 422
return
end
if @gift_card.user
if @gift_card.user == current_user
render json: {errors:{gift_card: ['already redeemed by you']}}, status: 422
return
else
render json: {errors:{gift_card: ['already redeemed by another']}}, status: 422
return
end
end
@gift_card.user = current_user
@gift_card.save
if @gift_card.errors.any?
respond_with_model(@gift_card)
return
else
# apply gift card items to everything in shopping cart
current_user.reload
ShoppingCart.apply_gifted_jamtracks(current_user)
render json: {gifted_jamtracks:current_user.gifted_jamtracks}, status: 200
end
end
###################### RECORDINGS #######################
# def recording_index
# @recordings = User.recording_index(current_user, params[:id])

View File

@ -208,5 +208,10 @@ class LandingsController < ApplicationController
render 'affiliate_program', layout: 'web'
end
def redeem_giftcard
@no_landing_tag = true
@landing_tag_play_learn_earn = true
render 'redeem_giftcard', layout: 'web'
end
end

View File

@ -1,6 +1,6 @@
object @jam_track
attributes :id, :name, :description, :recording_type, :original_artist, :songwriter, :publisher, :sales_region, :price, :version, :duration
attributes :id, :name, :description, :recording_type, :original_artist, :songwriter, :publisher, :sales_region, :price, :version, :duration, :year, :plan_code
node :genres do |item|
item.genres.select(:description).map(&:description)

View File

@ -1,6 +1,6 @@
object @jam_track
attributes :id, :name, :description, :initial_play_silence, :original_artist, :version
attributes :id, :name, :description, :initial_play_silence, :original_artist, :version, :year, :duration
child(:genres) {
attributes :id, :description

View File

@ -1 +1,5 @@
extends "api_shopping_carts/show"
extends "api_shopping_carts/show"
node :show_free_jamtrack do
any_user.show_free_jamtrack?
end

View File

@ -0,0 +1,3 @@
node :show_free_jamtrack do
any_user.show_free_jamtrack?
end

View File

@ -0,0 +1,3 @@
node :show_free_jamtrack do
any_user.show_free_jamtrack?
end

View File

@ -11,15 +11,15 @@ end
# give back more info if the user being fetched is yourself
if @user == current_user
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :show_whats_next_count, :subscribe_email, :auth_twitter, :new_notifications, :sales_count, :reuse_card, :purchased_jamtracks_count, :first_downloaded_client_at, :created_at, :first_opened_jamtrack_web_player
attributes :email, :original_fpfile, :cropped_fpfile, :crop_selection, :session_settings, :show_whats_next, :show_whats_next_count, :subscribe_email, :auth_twitter, :new_notifications, :sales_count, :reuse_card, :purchased_jamtracks_count, :first_downloaded_client_at, :created_at, :first_opened_jamtrack_web_player, :gifted_jamtracks, :has_redeemable_jamtrack
node :geoiplocation do |user|
geoiplocation = current_user.geoiplocation
geoiplocation.info if geoiplocation
end
node :free_jamtrack do |user|
Rails.application.config.one_free_jamtrack_per_user && user.has_redeemable_jamtrack
node :show_free_jamtrack do |user|
user.show_free_jamtrack?
end
node :show_jamtrack_guide do |user|

View File

@ -1,86 +0,0 @@
#jamtrackScreen.screen.secondary.no-login-required layout='screen' layout-id='jamtrackBrowse'
.content
.content-head
.content-icon=image_tag("content/icon_jamtracks.png", height:19, width:19 )
h1 jamtracks
=render "screen_navigation"
.content-body
=form_tag('', {:id => 'jamtrack-find-form', :class => 'inner-content'}) do
=render(:partial => "web_filter", :locals => {:search_type => Search::PARAM_JAMTRACK})
.filter-body
.content-body-scroller
.profile-wrapper
h2 shop for jamtracks
table.generaltable.jamtrack-table
thead
tr
th.jamtrack-detail JAMTRACK
th.jamtrack-tracks TRACKS INCLUDED / PREVIEW
th.jamtrack-action SHOP
tbody.jamtrack-content
a.btn-next-pager href="/api/jamtracks?page=1" Next
.end-of-jamtrack-list.end-of-list="No more Jamtracks"
script#template-jamtrack type='text/template'
tr.jamtrack-record jamtrack-id="{{data.jamtrack.id}}"
td.jamtrack-detail
.jamtrack-name
| "{{data.jamtrack.name}}"
.jamtrack-original-artist
| by {{data.jamtrack.original_artist}}
br clear="all"
.clearall.detail-label.extra.hidden.song-writer
| Songwriters:
.detail-value.extra.hidden
| {{data.jamtrack.songwriter}}
.clearall.detail-label.extra.hidden
| Publishers:
.detail-value.extra.hidden
| {{data.jamtrack.publisher}}
.clearall.detail-label.extra.hidden
| Genre:
.detail-value.extra.hidden
| {{data.jamtrack.genres[0]}}
.clearall.detail-label.extra.hidden
| Version:
.detail-value.extra.hidden
| {{data.jamtrack.recording_type}}
td.jamtrack-tracks
.detail-arrow
.jamtrack-detail-btn
="{% if (data.expanded) { %}"
| hide tracks
a.details-arrow.arrow-up
="{% } else { %}"
| show all tracks
a.details-arrow.arrow-down
="{% } %}"
="{% _.each(data.jamtrack.tracks, function(track) { %}"
.jamtrack-track.hidden jamtrack-track-id="{{track.id}}"
/ .instrument-desc
/ | {{track.instrument_desc}}
/.track-instrument
.jamtrack-preview
.clearall
="{% }); %}"
td.jamtrack-action
.jamtrack-action-container
.jamtrack-actions
/ a.play-button href="#" data-jamtrack-id="{{data.jamtrack.id}}"
/ =image_tag "shared/play_button.png"
.jamtrack-price class="{{data.free_state}}"
| {{"$ " + data.jamtrack.price}}
="{% if (data.is_free) { %}"
a.jamtrack-add-cart.button-orange.is_free href="#" data-jamtrack-id="{{data.jamtrack.id}}" GET IT FREE!
="{% } else if (data.jamtrack.purchased) { %}"
a.jamtrack-add-cart-disabled.button-grey.button-disabled href="javascript:void(0)" PURCHASED
="{% } else if (data.jamtrack.added_cart) { %}"
a.jamtrack-add-cart-disabled.button-grey.button-disabled href="client#/shoppingCart" ALREADY IN CART
="{% } else { %}"
a.jamtrack-add-cart.button-orange href="#" data-jamtrack-id="{{data.jamtrack.id}}" ADD TO CART
="{% }; %}"
="{% if (data.jamtrack.sales_region==JK.AVAILABILITY_US) { %}"
.jamtrack-license
| This JamTrack available only to US customers. &nbsp;&nbsp;&nbsp;&nbsp;
a.license-us-why.orange href="#" why?
="{% }; %}"

View File

@ -1,290 +0,0 @@
div layout="screen" layout-id="order" id="orderScreen" class="screen secondary"
.content
.content-head
.content-icon= image_tag("content/icon_shopping_cart.png", {:height => 19, :width => 19})
h1 check out
= render "screen_navigation"
.content-body
#order_error.error.hidden
.content-body-scroller
.content-wrapper
.checkout-navigation-bar
.checkout-payment-info
form class="payment-info" id="checkout-payment-info"
.billing-address
h2.billing-caption Billing Address
#divBillingFirstName
.billing-label
label for="billing-first-name" First Name:
.billing-value
input type="text" id="billing-first-name"
.clearall
#divBillingLastName
.billing-label
label for="billing-last-name" Last Name:
.billing-value
input type="text" id="billing-last-name"
.clearall
#divBillingAddress1
.billing-label
label for="billing-address1" Address 1:
.billing-value
input type="text" id="billing-address1"
.clearall
.billing-label
label for="billing-address2" Address 2:
.billing-value
input type="text" id="billing-address2"
.clearall
#divBillingCity
.billing-label
label for="billing-city" City:
.billing-value
input type="text" id="billing-city"
.clearall
#divBillingState
.billing-label
label for="billing-state" State/Region:
.billing-value
input type="text" id="billing-state"
.clearall
#divBillingZip
.billing-label
label for="billing-zip" Zip:
.billing-value
input type="text" id="billing-zip"
.clearall
#divBillingCountry
.billing-label
label for="billing-country" Country:
.billing-value
input type="text" id="billing-country"
.clearall
.payment-method
h2.payment-method-caption Payment Method
b Enter Card Information
br
#divCardName
.card-label.mt10
label for="card-name" Name of Card:
.card-value.mt10
input type="text" id="card-name"
.clearall
#divCardNumber
.card-label.mt10
label for="card-number" Card Number:
.card-value.mt10
input type="text" id="card-number"
.clearall
#divCardExpiry
.card-label.mt10 Expiration Date:
.card-value.mt10
=date_select("card", "expire-date", use_two_digit_numbers: true, discard_day: true, :start_year => Time.now.year, :end_year => Time.now.year + 18, :order => [:month, :day, :year], :default => -25.years.from_now, :html=>{:class => "account-profile-birthdate", :id=>"card-expiry"} )
.clearall
#divCardVerify
.card-label.mt10
label for="card-verify" Verification Value:
.card-value.mt10
input type="text" id="card-verify"
.clearall
.card-label.mt15
.card-value.mt15
.save-card-checkbox.ichecbuttons
input type="checkbox" id="save-card" name="save-card" checked="checked"
.divSaveCardHelper
label for="save-card" Save card for future use
.clearall
.clearall
.clearall
.action-bar.mt15
.shipping-address
h2.shipping-address-label Shipping Address
.shipping-as-billing.ichecbuttons
input type="checkbox" id="shipping-as-billing" name="shipping-as-billing" checked="checked"
.divBillingHelper
label for="shipping-as-billing" Same as billing address
.clearall
.shipping-address-detail.hidden
#divShippingFirstName
.shipping-label
label for="shipping-first-name" First Name:
.shipping-value
input type="text" id="shipping-first-name"
.clearall
#divShippingLastName
.shipping-label
label for="shipping-last-name" Last Name:
.shipping-value
input type="text" id="shipping-last-name"
.clearall
#divShippingAddress1
.shipping-label
label for="shipping-address1" Address 1:
.shipping-value
input type="text" id="shipping-address1"
.clearall
.shipping-label
label for="shipping-address2" Address 2:
.shipping-value
input type="text" id="shipping-address2"
.clearall
#divShippingCity
.shipping-label
label for="shipping-city" City:
.shipping-value
input type="text" id="shipping-city"
.clearall
#divShippingState
.shipping-label
label for="shipping-state" State/Region:
.shipping-value
input type="text" id="shipping-state"
.clearall
#divShippingZip
.shipping-label
label for="shipping-zip" Zip:
.shipping-value
input type="text" id="shipping-zip"
.clearall
#divShippingCountry
.shipping-label
label for="shipping-country" Country:
.shipping-value
input type="text" id="shipping-country"
.clearall
.right.mt30
a href="#" id="payment-info-help" class="button-grey" HELP
a href="#" id="payment-info-next" class="button-orange" NEXT
.clearall
.order-panel.hidden
.order-header.left
h2 Review Your Order
.mt5
span By placing your order, you agree to JamKazam's
'
a href="/corp/terms" terms of service
'
span and
'
a href="/corp/returns" returns policy
span .
.right.mt10
a href="#" class="button-grey" HELP
.clearall
.order-content
.thanks-panel.hidden
h2 Thank you for your order!
br
.thanks-detail We'll send you an email confirming your order shortly.
br
.thanks-detail.jam-tracks-in-browser.hidden
| To play your purchased JamTrack, launch the JamKazam application and open the JamTrack while in a session.
.thanks-detail.purchased-jam-track.hidden
h2.purchased-jam-track-header Downloading Your Purchased JamTracks
span Each JamTrack will be downloaded sequentially.
br
span.notice Note that you do not have to wait for this to complete in order to use your JamTrack later.
br.clear
ul.purchased-list
script type='text/template' id='template-order-content'
.order-left-page
.payment-info-page
.address-info
b.left Billing Address
a.left.ml5.change-payment-info href="#" change
.clearall
span.mt5= "{{data.billing_info.first_name}} {{data.billing_info.last_name}}"
br
span.mt5= "{{data.billing_info.address1}}"
br
span.mt5= "{{data.billing_info.address2}}"
br
br
b.left Shipping Address
a.left.ml5.change-payment-info href="#" change
.clearall
= "{% if (data.shipping_as_billing) { %}"
span.mt5 same as billing address
= "{% } else { %}"
span.mt5= "{{data.shipping_info.first_name}} {{data.shipping_info.last_name}}"
br
span.mt5= "{{data.shipping_info.address1}}"
br
span.mt5= "{{data.shipping_info.address2}}"
= "{% } %}"
br
.payment-method-info
b.left Payment Method
a.left.ml5.change-payment-info href="#" change
.clearall
/= image_tag ''
="Ending in: {{data.billing_info.number.slice(-4)}}"
.clearall
.order-items-page
.cart-items
.cart-item-caption#header
span Your order includes:
.cart-item-price
span style="text-decoration: underline;" Price
.cart-item-quantity
span style="text-decoration: underline;" Quantity
.clearall
= "{% if (data.carts.length == 0) { %}"
.no-cart-items You have no orders now.
= "{% } %}"
= "{% _.each(data.carts, function(cart) { %}"
.cart-item cart-id="{{cart.id}}"
.cart-item-caption
= "{{cart.cart_type}}: {{cart.product_info.name}}"
.cart-item-price
= "$ {{cart.product_info.price}}"
.cart-item-quantity
= "{{cart.quantity}}"
.clearall
= "{% }); %}"
.clearall
.order-right-page
a href="#" class="button-orange place-order" PLACE YOUR ORDER
br
br
b.mt10 Order Summary:
br
.left Items:
.right= "${{data.sub_total}}"
.clearall
.left Shipping & handling
.right $0.00
br
hr
.left Total before Tax:
.right= "${{data.sub_total}}"
.clearall
.left Taxes:
.right= "${{data.taxes}}"
.clearall
br
hr
b.order-total
.left Order Total:
.right= "${{data.grand_total}}"
.clearall
br
div style="text-align: left;"
span By placing your order, you agree to JamKazam's
'
a href="/corp/terms" terms of service
'
span and
'
a href="/corp/returns" returns policy
span .
script type='text/template' id='template-purchased-jam-track'
li data-jam-track-id="{{data.jam_track_id}}"

View File

@ -27,6 +27,9 @@ div layout="screen" layout-id="redeemComplete" id="redeemCompleteScreen" class="
.download-jamkazam
| Click Here to Get the Free JamKazam Application
a.back-to-browsing href="/client#/jamtrack"
| or click here to browse more jamtracks
.jam-tracks-in-client.hidden
h2 Congratulations on getting your JamTrack!

View File

@ -50,7 +50,6 @@
<%= render "checkout_complete" %>
<%= render "redeem_signup" %>
<%= render "redeem_complete" %>
<%= render "order" %>
<%= render "feed" %>
<%= render "bands" %>
<%= render "musicians" %>
@ -124,14 +123,14 @@
JK.currentUserName = '<%= current_user.name %>';
JK.currentUserMusician = '<%= current_user.musician %>';
JK.currentUserAdmin = <%= current_user.admin %>;
JK.currentUserFreeJamTrack = <%= APP_CONFIG.one_free_jamtrack_per_user && current_user.has_redeemable_jamtrack %>
JK.currentUserFreeJamTrack = <%= current_user.show_free_jamtrack? %>
<% else %>
JK.currentUserId = null;
JK.currentUserAvatarUrl = null;
JK.currentUserName = null;
JK.currentUserMusician = null;
JK.currentUserAdmin = false;
JK.currentUserFreeJamTrack = <%= APP_CONFIG.one_free_jamtrack_per_user && anonymous_user.nil? ? false : anonymous_user.has_redeemable_jamtrack%>
JK.currentUserFreeJamTrack = <%= anonymous_user.nil? ? false : anonymous_user.show_free_jamtrack? %>
<% end %>

View File

@ -0,0 +1,5 @@
- provide(:page_name, 'landing_page full redeem_giftcard')
- provide(:description, 'Here you can redeem a gift card and associate it with your JamKazam account.')
- provide(:title, 'Redeem a Gift Card')
= react_component 'RedeemGiftCardPage'

View File

@ -56,15 +56,13 @@
<% if @websocket %>
if(!window.opener) {
JK.JamServer.connect() // singleton here defined in JamServer.js
.done(function() {
console.log("websocket connected")
})
.fail(function() {
//console.log("websocket failed to connect")
});
}
JK.JamServer.connect() // singleton here defined in JamServer.js
.done(function() {
console.log("websocket connected")
})
.fail(function() {
//console.log("websocket failed to connect")
});
<% end %>
})

View File

@ -113,13 +113,13 @@
JK.currentUserAvatarUrl = JK.resolveAvatarUrl('<%= current_user.photo_url %>');
JK.currentUserName = '<%= current_user.name %>';
JK.currentUserMusician = '<%= current_user.musician %>';
JK.currentUserFreeJamTrack = <%= APP_CONFIG.one_free_jamtrack_per_user && current_user.has_redeemable_jamtrack %>
<% else %>
JK.currentUserId = null;
JK.currentUserFreeJamTrack = <%= current_user.show_free_jamtrack? %>
<% else %>
JK.currentUserId = null;
JK.currentUserAvatarUrl = null;
JK.currentUserName = null;
JK.currentUserMusician = null;
JK.currentUserFreeJamTrack = <%= APP_CONFIG.one_free_jamtrack_per_user && anonymous_user.nil? ? false : anonymous_user.has_redeemable_jamtrack%>
JK.currentUserFreeJamTrack = <%= anonymous_user.nil? ? false : anonymous_user.show_free_jamtrack? %>
<% end %>
</script>

View File

@ -372,6 +372,8 @@ if defined?(Bundler)
config.time_shift_style = :sbsms # or sox
config.middleware.use Rack::Deflater
config.download_tracker_day_range = 30
config.max_user_ip_address = 10
config.max_multiple_users_same_ip = 2

View File

@ -11,7 +11,6 @@ if Rails.env == "development" && Rails.application.config.bootstrap_dev_users
User.create_dev_user("David", "Wilson", "david@jamkazam.com", "jam123", "Austin", "TX", "US", nil, 'http://www.jamkazam.com/assets/avatars/avatar_david.jpg')
User.create_dev_user("Jonathan", "Kolyer", "jonathan@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
User.create_dev_user("Oswald", "Becca", "os@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
User.create_dev_user("Anthony", "Davis", "anthony@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
User.create_dev_user("Steven", "Miers", "steven@jamkazam.com", "jam123", "Austin", "TX", "US", nil, nil)
end

View File

@ -21,6 +21,8 @@ SampleApp::Application.routes.draw do
match '/signin', to: 'sessions#create', via: :post
match '/signout', to: 'sessions#destroy', via: :delete
match '/redeem_giftcard', to: 'landings#redeem_giftcard', via: :get
# landing pages
match '/landing/wb', to: 'landings#watch_bands', via: :get, as: 'landing_wb'
match '/landing/wo', to: 'landings#watch_overview', via: :get, as: 'landing_wo'
@ -452,6 +454,9 @@ SampleApp::Application.routes.draw do
match '/users/:id/syncs/:user_sync_id' => 'api_user_syncs#show', :via => :get
match '/users/:id/syncs/deletables' => 'api_user_syncs#deletables', :via => :post
# giftcards
match '/users/:id/gift_cards' => 'api_users#redeem_giftcard', :via => :post
# bands
@ -511,6 +516,7 @@ SampleApp::Application.routes.draw do
match '/search' => 'api_search#index', :via => :get
match '/search/musicians' => 'api_search#musicians', :via => [:get, :post]
match '/search/bands' => 'api_search#bands', :via => [:get, :post]
match '/search/jam_tracks' => 'api_search#jam_tracks', :via => [:get, :post]
# join requests
match '/join_requests/:id' => 'api_join_requests#show', :via => :get, :as => 'api_join_request_detail'

View File

@ -64,6 +64,19 @@ namespace :db do
make_recording
end
task populate_jam_track_genres: :environment do
genres = Genre.all
genres = genres.sample(genres.count * 0.75)
JamTrack.all.each do |jt|
rand(1..4).downto(1) do |nn|
gjt = GenreJamTrack.new
gjt.genre_id = genres.sample.id
gjt.jam_track_id = jt.id
gjt.save
end
end
end
# invoke like:
# email=seth@jamkazam.com bundle exec rake db:populate_jam_track

View File

@ -29,6 +29,7 @@ class UserManager < BaseManager
any_user = options[:any_user]
signup_hint = options[:signup_hint]
affiliate_partner = options[:affiliate_partner]
gift_card = options[:gift_card]
recaptcha_failed = false
unless options[:skip_recaptcha] # allow callers to opt-of recaptcha
@ -72,7 +73,8 @@ class UserManager < BaseManager
affiliate_referral_id: affiliate_referral_id,
any_user: any_user,
signup_hint: signup_hint,
affiliate_partner: affiliate_partner)
affiliate_partner: affiliate_partner,
gift_card: gift_card)
user
end

View File

@ -25,11 +25,14 @@ describe ApiShoppingCartsController do
it "add_jamtrack" do
post :add_jamtrack, {:format => 'json', id: jam_track.id}
response.status.should == 201
response.status.should == 200
end
it "index" do
cart = ShoppingCart.create(user, jam_track)
cart.errors.any?.should be_false
user.reload
user.shopping_carts.count.should eq(1)
get :index, {:format => 'json'}
response.status.should == 200
@ -41,7 +44,7 @@ describe ApiShoppingCartsController do
it "remove_cart" do
cart = ShoppingCart.create(user, jam_track)
delete :remove_cart, {:format => 'json', id: cart.id}
response.status.should == 204
response.status.should == 200
ShoppingCart.find_by_id(cart.id).should be_nil
end
@ -64,7 +67,9 @@ describe ApiShoppingCartsController do
it "add_jamtrack" do
post :add_jamtrack, {:format => 'json', id: jam_track.id}
response.status.should == 201
response.status.should == 200
user.reload
user.shopping_carts.count.should eql(1)
end
it "index" do
@ -80,7 +85,7 @@ describe ApiShoppingCartsController do
it "remove_cart" do
cart = ShoppingCart.create(user, jam_track)
delete :remove_cart, {:format => 'json', id: cart.id}
response.status.should == 204
response.status.should == 200
ShoppingCart.find_by_id(cart.id).should be_nil
end

View File

@ -5,12 +5,134 @@ describe ApiUsersController do
let (:user) { FactoryGirl.create(:user) }
let (:conn) { FactoryGirl.create(:connection, user: user, last_jam_audio_latency: 5) }
let (:jam_track) { FactoryGirl.create(:jam_track)}
before(:each) do
controller.current_user = user
end
describe "redeem_giftcard" do
let!(:gift_card) {FactoryGirl.create(:gift_card)}
it "can succeed" do
post :redeem_giftcard, id:user.id, gift_card: gift_card.code, format:'json'
response.should be_success
user.reload
gift_card.reload
user.gift_cards.should eq([gift_card])
user.gifted_jamtracks.should eq(10)
gift_card.user.should eq(user)
end
it "indicates if you've redeemed it" do
gift_card.user = user
gift_card.save!
post :redeem_giftcard, id:user.id, gift_card: gift_card.code, format:'json'
response.status.should eq(422)
error_data = JSON.parse(response.body)
error_data['errors']['gift_card'].should eq(["already redeemed by you"])
user.reload
gift_card.reload
user.gift_cards.should eq([gift_card])
user.gifted_jamtracks.should eq(10)
gift_card.user.should eq(user)
end
it "indicates if someone else has redeemed it" do
user2 = FactoryGirl.create(:user)
gift_card.user = user2
gift_card.save!
post :redeem_giftcard, id:user.id, gift_card: gift_card.code, format:'json'
response.status.should eq(422)
error_data = JSON.parse(response.body)
error_data['errors']['gift_card'].should eq(["already redeemed by another"])
user.reload
gift_card.reload
user.gift_cards.should eq([])
user.gifted_jamtracks.should eq(0)
gift_card.user.should eq(user2)
end
it "marks free shopping cart item as free" do
# sort of a 'do nothing' really
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
cart1.marked_for_redeem.should eq(1)
post :redeem_giftcard, id:user.id, gift_card: gift_card.code, format:'json'
response.should be_success
user.reload
gift_card.reload
user.gift_cards.should eq([gift_card])
user.gifted_jamtracks.should eq(10)
gift_card.user.should eq(user)
cart1.reload
cart1.marked_for_redeem.should eq(1)
end
it "marks non-free shopping cart item as free" do
# sort of a 'do nothing' really
user.has_redeemable_jamtrack = false
user.save!
cart1 = ShoppingCart.add_jam_track_to_cart(user, jam_track)
cart1.marked_for_redeem.should eq(0)
post :redeem_giftcard, id:user.id, gift_card: gift_card.code, format:'json'
response.should be_success
user.reload
gift_card.reload
user.gift_cards.should eq([gift_card])
user.gifted_jamtracks.should eq(10)
gift_card.user.should eq(user)
cart1.reload
cart1.marked_for_redeem.should eq(1)
end
it "leaves shopping cart alone if too many items in it for size of new gift card" do
# sort of a 'do nothing' really
user.has_redeemable_jamtrack = false
user.save!
11.times do |i|
jamtrack = FactoryGirl.create(:jam_track)
cart1 = ShoppingCart.add_jam_track_to_cart(user, jamtrack)
cart1.marked_for_redeem.should eq(0)
end
post :redeem_giftcard, id:user.id, gift_card: gift_card.code, format:'json'
response.should be_success
user.reload
gift_card.reload
user.gift_cards.should eq([gift_card])
user.gifted_jamtracks.should eq(10)
gift_card.user.should eq(user)
user.shopping_carts.each do |cart|
cart.marked_for_redeem.should eq(0)
end
end
end
describe "create" do
it "successful" do
email = 'user_create1@jamkazam.com'

View File

@ -21,6 +21,8 @@ FactoryGirl.define do
subscribe_email true
last_jam_audio_latency 5
reuse_card true
has_redeemable_jamtrack true
gifted_jamtracks 0
factory :fan do
musician false
@ -837,4 +839,9 @@ FactoryGirl.define do
factory :affiliate_legalese, class: 'JamRuby::AffiliateLegalese' do
legalese Faker::Lorem.paragraphs(6).join("\n\n")
end
factory :gift_card, class: 'JamRuby::GiftCard' do
sequence(:code) {|n| n.to_s}
card_type GiftCard::JAM_TRACKS_10
end
end

View File

@ -5,6 +5,7 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
let(:user) { FactoryGirl.create(:user) }
let(:jamtrack_acdc_backinblack) { @jamtrack_acdc_backinblack }
let(:jamtrack_pearljam_evenflow) { @jamtrack_pearljam_evenflow }
let(:jamtrack_led_zeppelin_kashmir) {@jamtrack_led_zeppelin_kashmir}
let(:billing_info) {
{
@ -35,8 +36,11 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
@recurlyClient = RecurlyClient.new
@created_accounts = []
JamTrack.delete_all
@jamtrack_acdc_backinblack = FactoryGirl.create(:jam_track, name: 'Back in Black', original_artist: 'AC/DC', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack')
@jamtrack_pearljam_evenflow = FactoryGirl.create(:jam_track, name: 'Even Flow', original_artist: 'Pearl Jam', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-pearljam-evenflow')
@jamtrack_led_zeppelin_kashmir = FactoryGirl.create(:jam_track, name: 'Kashmir', original_artist: 'Led Zeppelin', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-led-zeppelin-kashmir')
# make sure plans are there
@recurlyClient.create_jam_track_plan(@jamtrack_acdc_backinblack) unless @recurlyClient.find_jam_track_plan(@jamtrack_acdc_backinblack)
@ -1225,4 +1229,98 @@ describe "Checkout", :js => true, :type => :feature, :capybara_feature => true d
acdc_sale.sale.should eq(sale)
end
end
describe "gift cards" do
it "user has both redeemable jamtrack and gift card jamtracks" do
jamtrack_led_zeppelin_kashmir.touch
jamtrack_pearljam_evenflow.touch
user.gifted_jamtracks = 1
user.save!
fast_signin(user, "/client?song=#{jamtrack_acdc_backinblack.name}#/jamtrack/search")
find('h1', text: 'jamtracks')
find("a.jamtrack-add-cart[data-jamtrack-id=\"#{jamtrack_acdc_backinblack.id}\"]", text: 'GET IT FREE!').trigger(:click)
find('.jam-tracks-in-browser')
user.reload
user.has_redeemable_jamtrack.should be_false
find('a.back-to-browsing').trigger(:click)
find('h1', text: 'search jamtracks')
find('.search-controls .Select-control').trigger(:mousedown)
# wait for the 'Type to search' prompt to show
find('.search-controls .Select-search-prompt')
find('.search-by-string-btn').trigger(:click)
find('.jamtrack-record[data-jamtrack-id="' + jamtrack_led_zeppelin_kashmir.id + '"]')
find("a.jamtrack-add-cart[data-jamtrack-id=\"#{jamtrack_led_zeppelin_kashmir.id}\"]", text: 'GET IT FREE!').trigger(:click)
find('.jam-tracks-in-browser')
sleep 2
user.reload
user.has_redeemable_jamtrack.should be_false
user.gifted_jamtracks.should eq(0)
find('a.back-to-browsing').trigger(:click)
find('h1', text: 'search jamtracks')
find('.search-controls .Select-control').trigger(:mousedown)
# wait for the 'Type to search' prompt to show
find('.search-controls .Select-search-prompt')
find('.search-by-string-btn').trigger(:click)
find('.jamtrack-record[data-jamtrack-id="' + jamtrack_pearljam_evenflow.id + '"]')
find("a.jamtrack-add-cart[data-jamtrack-id=\"#{jamtrack_pearljam_evenflow.id}\"]", text: 'ADD TO CART').trigger(:click)
find('h1', text: 'shopping cart')
find('.cart-item-caption', text: "JamTrack: #{jamtrack_pearljam_evenflow.name}")
find('.cart-item-price', text: "$ #{jamtrack_pearljam_evenflow.price}")
find('.shopping-sub-total', text:"Subtotal:$ #{jamtrack_pearljam_evenflow.price}")
# attempt to checkout
find('a.button-orange', text: 'PROCEED TO CHECKOUT').trigger(:click)
# should be at payment page
# this should take us to the payment screen
find('p.payment-prompt')
# fill out all billing info and account info
fill_in 'billing-first-name', with: 'Seth'
fill_in 'billing-last-name', with: 'Call'
fill_in 'billing-address1', with: '10704 Buckthorn Drive'
fill_in 'billing-city', with: 'Austin'
fill_in 'billing-state', with: 'Texas'
fill_in 'billing-zip', with: '78759'
fill_in 'card-number', with: '4111111111111111'
fill_in 'card-verify', with: '012'
#jk_select('US', '#checkoutPaymentScreen #divBillingCountry #billing-country')
find('#payment-info-next').trigger(:click)
# should be taken straight to order page
# now see order page, and everything should no longer appear free
find('p.order-prompt')
find('.order-items-value.order-total', text:'$1.99')
find('.order-items-value.shipping-handling', text:'$0.00')
find('.order-items-value.sub-total', text:'$1.99')
find('.order-items-value.taxes', text:'$0.16')
find('.order-items-value.grand-total', text:'$2.15')
# click the ORDER button
find('.place-order-center a.button-orange.place-order').trigger(:click)
# and now we should see confirmation, and a notice that we are in a normal browser
find('.thanks-detail.jam-tracks-in-browser')
user.reload
sleep 3 # challenge to all comers! WHY DO I HAVE TO SLEEP FOR THIS ASSERTION TO BE TRUE! GAH . and 1 second won't do it
user.reload
user.has_redeemable_jamtrack.should be_false
user.gifted_jamtracks.should eq(0)
user.purchased_jamtracks_count.should eq(3)
end
end
end

View File

@ -94,8 +94,6 @@ describe "Individual JamTrack", :js => true, :type => :feature, :capybara_featur
find('.tracks.previews[data-id="' + track.id + '"] .instrument-name', text:track.instrument.description)
end
end
find('a.cta-free-jamtrack').trigger(:click)
find('a.browse-all')['href'].should eq("/client?search=#/jamtrack/search")
find('button.cta-button').trigger(:click)
find('.browse-jamtracks', text: 'search jamtracks')

View File

@ -2,7 +2,7 @@ require 'spec_helper'
describe "JamTrack Shopping", :js => true, :type => :feature, :capybara_feature => true do
let(:user) { FactoryGirl.create(:user, has_redeemable_jamtrack: false) }
let(:user) { FactoryGirl.create(:user, gifted_jamtracks: 0, has_redeemable_jamtrack: false) }
let(:jt_us) { FactoryGirl.create(:jam_track, :name=>'jt_us', sales_region: 'Worldwide', make_track: true, original_artist: "foobar") }
let(:jt_ww) { FactoryGirl.create(:jam_track, :name=>'jt_ww', sales_region: 'Worldwide', make_track: true, original_artist: "barfoo") }
let(:jt_rock) { FactoryGirl.create(:jam_track, :name=>'jt_rock', genres: [JamRuby::Genre.find('rock')], make_track: true, original_artist: "badfood") }

View File

@ -0,0 +1,169 @@
require 'spec_helper'
# tests what happens when the websocket connection goes away
describe "Redeem Gift Card", :js => true, :type => :feature, :capybara_feature => true do
subject { page }
let(:user1) { FactoryGirl.create(:user) }
let(:jamtrack_acdc_backinblack) { @jamtrack_acdc_backinblack }
let(:gift_card) {FactoryGirl.create(:gift_card)}
before(:all) do
User.delete_all
JamTrack.delete_all
@jamtrack_acdc_backinblack = FactoryGirl.create(:jam_track, name: 'Back in Black', original_artist: 'AC/DC', sales_region: 'United States', make_track: true, plan_code: 'jamtrack-acdc-backinblack')
end
describe "not logged in" do
it "suceeeds" do
visit '/redeem_giftcard'
find('h2', text:'Redeem Your Gift Card')
fill_in "code", with: gift_card.code
fill_in "email", with: "gifter1@jamkazam.com"
fill_in "password", with: "jam123"
find('.redeem-container ins').trigger(:click)
find('button.redeem-giftcard').trigger(:click)
find('.done-action a.go-browse').trigger(:click)
find('.no-free-jamtrack')
user = User.find_by_email("gifter1@jamkazam.com")
gift_card.reload
gift_card.user.should eq(user)
user.reload
user.gifted_jamtracks.should eq(10)
end
it "validates correctly" do
visit '/redeem_giftcard'
find('h2', text:'Redeem Your Gift Card')
find('button.redeem-giftcard').trigger(:click)
find('.errors.active', text: "Email can't be blank")
find('h2', text:'Redeem Your Gift Card')
fill_in "code", with: gift_card.code
fill_in "email", with: "gifter2@jamkazam.com"
fill_in "password", with: "jam123"
find('.redeem-container ins').trigger(:click)
find('button.redeem-giftcard').trigger(:click)
find('.done-action a.go-browse').trigger(:click)
find('.no-free-jamtrack')
user = User.find_by_email("gifter2@jamkazam.com")
gift_card.reload
gift_card.user.should eq(user)
user.reload
user.gifted_jamtracks.should eq(10)
end
it "converts shopping cart items to free" do
visit '/redeem_giftcard'
anon_user_id = page.driver.cookies["user_uuid"]
anon_user = AnonymousUser.new(anon_user_id.value, {})
cart = ShoppingCart.add_jam_track_to_cart(anon_user, jamtrack_acdc_backinblack)
cart.skip_mix_check = true
cart.marked_for_redeem = 0
cart.save!
find('h2', text:'Redeem Your Gift Card')
fill_in "code", with: gift_card.code
fill_in "email", with: "gifter_carted1@jamkazam.com"
fill_in "password", with: "jam123"
find('.redeem-container ins').trigger(:click)
find('button.redeem-giftcard').trigger(:click)
find('.done-action a.go-browse').trigger(:click)
find('.no-free-jamtrack')
cart.reload
cart.marked_for_redeem.should eq(1)
end
end
describe "logged in" do
it "succeeds" do
fast_signin(user1, '/redeem_giftcard')
find('h2', text:'Redeem Your Gift Card')
fill_in "code", with: gift_card.code
find('button.redeem-giftcard').trigger(:click)
find('.done-action a.go-browse').trigger(:click)
find('.no-free-jamtrack')
gift_card.reload
gift_card.user.should eq(user1)
user1.reload
user1.gifted_jamtracks.should eq(10)
end
end
describe "logged in" do
it "validates" do
fast_signin(user1, '/redeem_giftcard')
find('h2', text:'Redeem Your Gift Card')
find('button.redeem-giftcard').trigger(:click)
find('.errors.active', text: "Gift Card Code does not exist")
fill_in "code", with: gift_card.code
find('button.redeem-giftcard').trigger(:click)
find('.done-action a.go-browse').trigger(:click)
find('.no-free-jamtrack')
gift_card.reload
gift_card.user.should eq(user1)
user1.reload
user1.gifted_jamtracks.should eq(10)
end
it "converts shopping cart items to free" do
fast_signin(user1, '/redeem_giftcard')
cart = ShoppingCart.add_jam_track_to_cart(user1, jamtrack_acdc_backinblack)
cart.skip_mix_check = true
cart.marked_for_redeem = 0
cart.save!
visit '/redeem_giftcard'
find('h2', text:'Redeem Your Gift Card')
fill_in "code", with: gift_card.code
find('button.redeem-giftcard').trigger(:click)
find('.done-action a.go-browse').trigger(:click)
find('.no-free-jamtrack')
gift_card.reload
gift_card.user.should eq(user1)
user1.reload
user1.gifted_jamtracks.should eq(10)
cart.reload
cart.marked_for_redeem.should eq(1)
end
end
end

View File

@ -707,4 +707,77 @@ describe UserManager do
user.errors.any?.should be_false
end # it "passes when facebook signup"
end # describe "with nocaptcha"
describe "gift_card" do
let(:gift_card) {FactoryGirl.create(:gift_card)}
it "can succeed when specified" do
user = @user_manager.signup(remote_ip: "1.2.3.4",
first_name: "bob",
last_name: "smith",
email: "giftcard1@jamkazam.com",
password: "foobar",
password_confirmation: "foobar",
terms_of_service: true,
instruments: @instruments,
musician: true,
location: @loca,
signup_confirm_url: "http://localhost:3000/confirm",
gift_card: gift_card.code)
user.errors.any?.should be_false
gift_card.reload
gift_card.user.should eq(user)
user = User.find(user.id)
user.has_redeemable_jamtrack.should be_true
user.gifted_jamtracks.should eq(10)
user.gift_cards[0].should eq(gift_card)
end
it "will fail if invalid gift card code" do
user = @user_manager.signup(remote_ip: "1.2.3.4",
first_name: "bob",
last_name: "smith",
email: "giftcard2@jamkazam.com",
password: "foobar",
password_confirmation: "foobar",
terms_of_service: true,
instruments: @instruments,
musician: true,
location: @loca,
signup_confirm_url: "http://localhost:3000/confirm",
gift_card: '')
user.errors.any?.should be_true
user.errors["gift_card"].should eq(["not found"])
user.gifted_jamtracks.should eq(0)
gift_card.reload
gift_card.user.should be_nil
user.gift_cards.length.should eq(0)
end
it "will fail if used gift card" do
gift_card.user = FactoryGirl.create(:user)
user = @user_manager.signup(remote_ip: "1.2.3.4",
first_name: "bob",
last_name: "smith",
email: "giftcard2@jamkazam.com",
password: "foobar",
password_confirmation: "foobar",
terms_of_service: true,
instruments: @instruments,
musician: true,
location: @loca,
signup_confirm_url: "http://localhost:3000/confirm",
gift_card: '')
user.errors.any?.should be_true
user.errors["gift_card"].should eq(["not found"])
user.gifted_jamtracks.should eq(0)
gift_card.reload
gift_card.user.should be_nil
user.gift_cards.length.should eq(0)
end
end
end # test

View File

@ -627,7 +627,7 @@ describe "Active Music Session API ", :type => :api do
music_session = JSON.parse(last_response.body)[0]
# start a recording
post "/api/recordings/start", {:format => :json, :music_session_id => music_session['id'] }.to_json, "CONTENT_TYPE" => 'application/json'
post "/api/recordings/start", {:format => :json, :music_session_id => music_session['id'], record_video:false }.to_json, "CONTENT_TYPE" => 'application/json'
last_response.status.should eql(201)
# user 2 should not be able to join