Working on Affiliate Changes -- have updated account and links page

This commit is contained in:
Seth Call 2021-02-14 22:32:27 -06:00
parent ad2f29fe01
commit b3f4539b42
20 changed files with 147 additions and 107 deletions

View File

@ -72,6 +72,7 @@ gem 'jquery-ui-rails'# , '5.0.5' #, '4.2.1'
gem 'jquery-rails'# , '4.1.1' # both this and jquery-ui-rails are pinned; if you unpin, jquery/autocomplete is missing during precomplie
gem 'rails-jquery-autocomplete' # This is the maintained version of rails3-jquery-autocomplete
gem 'activeadmin' #, '1.0.0.pre4'# github: 'activeadmin', branch: 'master'
gem 'activeadmin-searchable_select'
gem 'mime-types', '1.25'
#gem 'meta_search'
gem 'fog'

View File

@ -47,6 +47,10 @@ GEM
ransack (>= 1.8.7)
sass (~> 3.1)
sprockets (< 4.1)
activeadmin-searchable_select (1.4.0)
activeadmin (>= 1.x, < 3)
jquery-rails (>= 3.0, < 5)
select2-rails (~> 4.0)
activeadmin_addons (1.7.1)
active_material
railties
@ -695,6 +699,7 @@ PLATFORMS
DEPENDENCIES
aasm
activeadmin
activeadmin-searchable_select
activeadmin_addons
amqp (= 0.9.8)
auto_strip_attributes (= 2.6.0)

View File

@ -9,22 +9,46 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
config.per_page = 50
config.paginate = true
form :partial => 'form'
#form :partial => 'form'
scope("Active", default: true) { |scope| scope.where('partner_user_id IS NOT NULL').order('referral_user_count desc') }
scope("Unpaid") { |partner| partner.unpaid }
form do |f|
f.inputs 'Fields' do
f.input(:partner_name, :input_html => { :maxlength => 128 })
f.input(:partner_user, as: :searchable_select, hint: 'This person is the owner of the affiliate. Has access to reporting info in account section of www.jamkazam.com')
f.input(:entity_type, :as => :select, :collection => AffiliatePartner::ENTITY_TYPES)
f.input(:rate)
end
f.actions
end
index do
# actions # use this for all view/edit/delete links
column 'User' do |oo| link_to(oo.partner_user.name, admin_user_path(oo.partner_user.id), {:title => oo.partner_user.name}) end
column 'Name' do |oo| oo.partner_name end
column 'Type' do |oo| oo.entity_type end
column 'Code' do |oo| oo.id end
column 'Referral Count' do |oo| oo.referral_user_count end
column 'Earnings' do |oo| sprintf("$%.2f", oo.cumulative_earnings_in_dollars) end
column 'Amount Owed' do |oo| sprintf("$%.2f", oo.due_amount_in_cents.to_f / 100.to_f) end
column 'User' do |oo|
link_to(oo.partner_user.name, admin_user_path(oo.partner_user.id), { :title => oo.partner_user.name })
end
column 'Name' do |oo|
oo.partner_name
end
column 'Type' do |oo|
oo.entity_type
end
column 'Code' do |oo|
oo.id
end
column 'Referral Count' do |oo|
oo.referral_user_count
end
column 'Earnings' do |oo|
sprintf("$%.2f", oo.cumulative_earnings_in_dollars)
end
column 'Amount Owed' do |oo|
sprintf("$%.2f", oo.due_amount_in_cents.to_f / 100.to_f)
end
column 'Pay Actions' do |oo|
link_to('Mark Paid', mark_paid_admin_affiliate_path(oo.id), :confirm => "Mark this affiliate as PAID?") if oo.unpaid
end
@ -32,7 +56,6 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
actions
end
action_item :only => [:show] do
link_to("Mark Paid",
mark_paid_admin_affiliate_path(resource.id),

View File

@ -1,5 +1,11 @@
ActiveAdmin.register JamRuby::User, :as => 'Users' do
searchable_select_options(scope: User.all,
text_attribute: :username,
filter: lambda do |term, scope|
scope.ransack(full_name_or_email_cont: term).result
end)
collection_action :autocomplete_user_email, :method => :get
actions :all, :except => [:destroy]

View File

@ -9,6 +9,7 @@
// require jquery.ui.autocomplete
//= require cocoon
//= require active_admin/base
//= require active_admin/searchable_select
// //= require autocomplete-rails
//= require base
//= require_tree .

View File

@ -15,6 +15,7 @@
// Active Admin's got SASS!
@import "active_admin/mixins";
@import "active_admin/base";
@import "active_admin/searchable_select";
// Overriding any non-variable SASS must be done after the fact.
// For example, to change the default status-tag color:

View File

@ -2,6 +2,7 @@
<%= f.semantic_errors *f.object.errors.keys %>
<%= f.inputs do %>
<%= f.input(:partner_name, :input_html => {:maxlength => 128}) %>
<%= f.input(:partner_user, as: :searchable_select, ajax: true, hint: 'The user that manages/owns this affiliate. They can see affiliate reports') %>
<%= f.input(:entity_type, :as => :select, :collection => AffiliatePartner::ENTITY_TYPES) %>
<%= f.input(:rate) %>
<% end %>

View File

@ -1,5 +1,6 @@
class CreateInitStructure < ActiveRecord::Migration
def up
ActiveRecord::Base.connection.execute(IO.read(File.expand_path("../../init_db.sql", __FILE__)))
# this can't apply in production or staging, -- and schema.rb captures this test/dev environments
#ActiveRecord::Base.connection.execute(IO.read(File.expand_path("../../init_db.sql", __FILE__)))
end
end

View File

@ -260,6 +260,7 @@ require "jam_ruby/models/affiliate_monthly_payment"
require "jam_ruby/models/affiliate_traffic_total"
require "jam_ruby/models/affiliate_referral_visit"
require "jam_ruby/models/affiliate_payment"
require "jam_ruby/models/affiliate_link"
require "jam_ruby/models/chat_message"
require "jam_ruby/models/shopping_cart"
require "jam_ruby/models/generic_state"

View File

@ -12,6 +12,7 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
has_many :traffic_totals, :class_name => 'JamRuby::AffiliateTrafficTotal', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
has_many :visits, :class_name => 'JamRuby::AffiliateReferralVisit', foreign_key: :affiliate_partner_id, inverse_of: :affiliate_partner
has_many :affiliate_distributions, :class_name => "JamRuby::AffiliateDistribution", foreign_key: :affiliate_referral_id
has_many :links, :class_name => "JamRuby::AffiliateLink", foreign_key: :affiliate_partner_id
attr_accessible :partner_name, :partner_code, :partner_user_id, :entity_type, :rate, as: :admin
ENTITY_TYPES = %w{ Individual Sole\ Proprietor Limited\ Liability\ Company\ (LLC) Partnership Trust/Estate S\ Corporation C\ Corporation Other }
@ -53,6 +54,7 @@ class JamRuby::AffiliatePartner < ActiveRecord::Base
before_save do |record|
record.address ||= ADDRESS_SCHEMA.clone
record.entity_type ||= ENTITY_TYPES.first
record.partner_user_id = nil if record.partner_user_id1 == '' #for activeadmin coercion
end
def display_name

View File

@ -22,6 +22,15 @@ namespace :db do
puts "#{ENV['RAILS_ENV']} database migrated."
end
desc "Rollback the database"
task :rollback do
steps = (ARGV[1] || "1").to_i
ActiveRecord::Base.establish_connection(db_config)
migrate_dir = File.expand_path("../../../../../db/migrate", __FILE__)
ActiveRecord::Migrator.rollback(migrate_dir, steps)
puts "#{ENV['RAILS_ENV']} database migrated."
end
desc "Drop the database"
task :drop do
raise "can not drop production database" if ENV['RAILS_ENV'] == 'production'
@ -48,7 +57,7 @@ namespace :db do
namespace :g do
desc "Generate migration"
task :migration do
name = ARGV[1] || raise("Specify name: rake g:migration your_migration")
name = ARGV[1] || raise("Specify name: rake db:g:migration your_migration")
timestamp = Time.now.strftime("%Y%m%d%H%M%S")
path = File.expand_path("../../../../../db/migrate/#{timestamp}_#{name}.rb", __FILE__)
migration_class = name.split("_").map(&:capitalize).join

View File

@ -13,7 +13,8 @@
var $screen = null;
function beforeShow(data) {
userId = data.id;
userId = context.JK.currentUserId
affiliatePartnerData = null;
}
@ -168,48 +169,14 @@
function updateLinks() {
var $select = $screen.find('select.link_type')
var value = $select.val()
logger.debug("value: " + value)
// affiliatePartnerData.account.id
var type = 'jamtrack_songs';
if(value == 'JamTrack Song') {
type = 'jamtrack_songs'
}
else if(value == 'JamTrack Band') {
type = 'jamtrack_bands'
}
else if(value == 'JamTrack General') {
type = 'jamtrack_general'
}
else if(value == 'JamKazam General') {
type = 'jamkazam'
}
else if(value == 'JamKazam Session') {
type = 'sessions'
}
else if(value == 'JamKazam Recording') {
type = 'recordings'
}
else if(value == 'Custom Link') {
type = 'custom_links'
}
$screen.find('.link-type-prompt').hide();
$screen.find('.link-type-prompt[data-type="' + type + '"]').show();
if(type == 'custom_links') {
$screen.find('table.links-table').hide();
$screen.find('.link-type-prompt[data-type="custom_links"] span.affiliate_id').text(affiliatePartnerData.account.id)
}
else {
rest.getLinks(type)
.done(populateLinkTable)
.fail(function() {
app.notify({text: 'Unable to fetch links. Please try again later.' })
})
}
rest.getLinks('all', affiliatePartnerData.account.id)
.done(populateLinkTable)
.fail(function() {
app.notify({text: 'Unable to fetch links. Please try again later.' })
})
}
function _renderAffiliateTab(theTab) {
@ -269,13 +236,21 @@
'postal_code': tab_content.find('#affiliate_partner_postal_code').val(),
'country': tab_content.find('#affiliate_partner_country').val()
},
'tax_identifier': tab_content.find('#affiliate_partner_tax_identifier').val()
'tax_identifier': tab_content.find('#affiliate_partner_tax_identifier').val(),
'paypal_id': tab_content.find('#affiliate_partner_paypal_id').val()
}
var button = $('#affiliate-profile-account-submit')
button.addClass("disabled")
rest.postAffiliatePartnerData(userId, affiliate_partner_data)
.done(postUpdateAffiliateAccountSuccess);
.done(postUpdateAffiliateAccountSuccess)
.fail(function(jqXHR) {
button.removeClass("disabled")
alert("Unable to update affiliate information.\n\n" + jqXHR.responseText)
})
}
function postUpdateAffiliateAccountSuccess(response) {
$('#affiliate-profile-account-submit').removeClass("disabled")
app.notify(
{
title: "Affiliate Account",

View File

@ -43,7 +43,7 @@ window.SupportPage = React.createClass({
{support_warning}
<div className="description">
The JamKazam help desk offers 1:1 help desk support only to our Gold and Platinum plan
subscribers. More information on subscription plans <a href="https://jamkazam.freshdesk.com/support/solutions/articles/66000122535-what-are-jamkazam-s-free-vs-premium-features-"
subscribers, as well as those in their initial 30-day trial period. More information on subscription plans <a href="https://jamkazam.freshdesk.com/support/solutions/articles/66000122535-what-are-jamkazam-s-free-vs-premium-features-"
target="_blank">can be found here</a>. If you are not a Gold or Platinum subscriber,
we'd suggest that you look for help in our <a href="https://jamkazam.freshdesk.com/support/home" target="_blank"> extensive knowledge base of
help articles</a>,

View File

@ -137,6 +137,8 @@
table.links-table {
min-width:100%;
margin-top:20px;
border-width: 1px 0 0 0;
border-style: solid;
th {
padding: 10px 0 20px;
@ -157,7 +159,9 @@
.url {
input {
font-family:courier;
background-color: transparent;
-webkit-box-shadow:none;
box-shadow:none;

View File

@ -113,7 +113,11 @@ class ApiController < ApplicationController
def affiliate_partner
if params[:affiliate_id]
@partner = AffiliatePartner.find(params[:affiliate_id])
if @partner.partner_user.nil?
if @partner.nil?
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
elsif @partner.partner_user.nil?
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
elsif !current_user.admin || @partner.partner_user != current_user
raise JamPermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR
end
elsif current_user

View File

@ -9,7 +9,27 @@ class ApiLinksController < ApiController
@log || Logging.logger[ApiLinksController]
end
def all
links = AffiliateLink.all.order(:created_at)
results = []
links.each do |link|
url = nil
if link.link.index('?')
url = link.link + '&' + affiliate_params.to_query
else
url = link.link + '?' + affiliate_params.to_query
end
results << {url: url,
target: link.name }
end
render json: results, status: 200
end
=begin
def jamtrack_song_index
@affiliate_params = affiliate_params()
@affiliate_params = affiliate_params('jamtrack-song')
results = []
@ -106,9 +126,12 @@ class ApiLinksController < ApiController
render json: results, status: 200
end
=end
private
def affiliate_params(campaign)
{utm_source:'affiliate', utm_medium: 'affiliate', utm_campaign: "#{Date.today.year}-affiliate-#{campaign}", affiliate: @partner.id}
def affiliate_params()
#{utm_source:'affiliate', utm_medium: 'affiliate', utm_campaign: "#{Date.today.year}-affiliate-#{campaign}", affiliate: @partner.id}#
# the above was deemed too noisy
{affiliate: @partner.id}
end
end

View File

@ -865,14 +865,16 @@ class ApiUsersController < ApiController
if request.post?
oo.address = params[:address]
oo.tax_identifier = params[:tax_identifier]
oo.paypal_id = params[:paypal_id]
oo.save!
render nothing: true
render json: {}, status: 200
elsif request.get?
result = {}
result['account'] = {
'address' => oo.address.clone,
'tax_identifier' => oo.tax_identifier,
'paypal_id' => oo.paypal_id,
'entity_type' => oo.entity_type,
'partner_name' => oo.partner_name,
'partner_id' => oo.partner_user_id,

View File

@ -12,7 +12,7 @@ class SupportsController < ApplicationController
gon.plan_code = current_user.subscription_plan_code
@title = "Help Desk"
@description = "The JamKazam help desk offers 1:1 help desk support only to in-trial and Gold and Platinum plan subscribers."
@description = "The JamKazam help desk offers 1:1 help desk support only to our Gold and Platinum plan subscribers, as well as those in their initial 30-day trial period."
render 'show', :layout => 'support'
end

View File

@ -41,12 +41,18 @@ script type="text/template" id="template-affiliate-partner-account"
.right-col
| We must have a complete mailing address and a valid tax ID in order to process and mail payments due to all affiliates.&nbsp;&nbsp;Per the terms of the affiliate agreement, if this information is not available within 90 days of the end of a calendar quarter, then any payment obligation due to the affiliate for such calendar quarter shall be considered fully and permanently discharged, and no further payment for such calendar quarter shall be due or payable to affiliate.
br
br
| For fastest receipt of payment, please specify a PayPal.Me account.
br
br
| So please provide this data, and be sure to keep it current!
.left-col
.affiliate-label Affiliate:
.w80= "{{ data.partner_name }} ({{data.entity_type}})"
br
br
.affiliate-label Street Address 1:
= text_field_tag('affiliate_partner_address1', "{{ data.address.address1 }}", {class: 'w60'})
br
@ -69,6 +75,18 @@ script type="text/template" id="template-affiliate-partner-account"
.affiliate-label Tax ID:
= text_field_tag('affiliate_partner_tax_identifier', "{{ data.tax_identifier }}", {class: 'w60'})
br
br
.affiliate-label PayPal.Me:
= text_field_tag('affiliate_partner_paypal_id', "{{ data.paypal_id }}", { class: 'w60' })
br
span style="padding-left:138px"
| A PayPal.Me link looks like&nbsp;
b paypal.me/YourName
| &nbsp;Please read more info&nbsp;
a href="https://www.paypal.com/us/smarthelp/article/What-is-PayPalMe-FAQ3025"
| about PayPal.Me
br
br
.spacer
.input-buttons
.right
@ -105,47 +123,9 @@ script type="text/template" id="template-affiliate-partner-signups"
script type="text/template" id="template-affiliate-partner-links"
.links
p.prompt
| This page provides you with lists of ready-to-use links you can use to direct your followers to JamKazam.&nbsp;
| These links include your unique affiliate ID, and users who follow these links to JamKazam and register for an account will be&nbsp;
| tagged with your affiliate ID, so that you will be paid on their purchase per the terms of the&nbsp;
a.affiliate-agreement rel='#' affiliate agreement
| . You can also find a&nbsp;
a.affiliate-link-page href='#' rel="external" single page listing all affiliate links
| &nbsp;which you can share with others who may need to share out affiliate links on your behalf.
.link-type-section
label Link Type:
select.link_type.easydropdown name="link_type"
option value="JamTrack Song" JamTrack Song
option value="JamTrack Band" JamTrack Band
option value="JamTrack General" JamTrack General
option value="JamKazam General" JamKazam General
option value="JamKazam Session" JamKazam Session
option value="JamKazam Recording" JamKazam Recording
option value="Custom Link" Custom Link
.link-type-prompt data-type='jamtrack_songs'
| These links take users directly to the "landing page" for a JamTrack - i.e. an individual piece of music. This would be a great fit, for example, if you have produced a YouTube tutorial on how to play a particular song, or if you have music notation for a particular song, etc.
.link-type-prompt data-type='jamtrack_bands'
| These links take users directly to the "landing page" for a band to promote all the JamTracks by that band, not just an individual song. This would be a great fit, for example, if you have music notation for several songs by a band, etc.
.link-type-prompt data-type='jamtrack_general'
| These links take users directly to a"landing page" that promotes JamTracks in general. This page will feature the song listed in the link as an example of a JamTrack, but the landing page is built to promote JamTracks in general versus promoting just the one song. This is a good fit if you want to let your followers know about JamTracks in general.
.link-type-prompt data-type='jamkazam'
| These links take users directly to a product page that promotes JamKazam more generally. This is a good fit if you want to let your followers know about JamKazam or its products in general.
.link-type-prompt data-type='sessions'
| These links take users to a page where they can listen to a session you have organized, and in which you are scheduled to perform, either alone or with others. This is a good fit if you can perform either solo or in a group with other musicians in a JamKazam session, and draw an audience to listen to your session. During the session, you can recommend that your audience members sign up for JamKazam from this page. Below is a list of your currently scheduled JamKazam sessions for which you may share links.
.link-type-prompt data-type='recordings'
| These links take users to a page with a recording you have made in a JamKazam session. This is a good fit if you have made a nice recording in a JamKazam session and can share it with your followers via Facebook, Twitter, email, and so on. Below is a list of your most recent JamKazam recordings for which you may share links.
.link-type-prompt data-type='custom_links'
p You may also link to any page on the JamKazam website and append the following text to the URL of the page to which you are linking:
p.example-link
| ?utm_source=affiliate&utm_medium=affiliate&utm_campaign=2015-affiliate-custom&affiliate=
span.affiliate_id
p For example, if you were linking to the JamKazam home page, you would combine https://www.jamkazam.com with the text above, and the result would be:
p.example-link
| https://www.jamkazam.com?utm_source=affiliate&utm_medium=affiliate&utm_campaign=2015-affiliate-custom&affiliate=
span.affiliate_id
| This page provides you with a list of ready-to-use links you can share with your followers to let them know about JamKazam. These links include your unique affiliate ID, and users who follow these links to JamKazam and sign up for a JamKazam account will be tagged with your affiliate ID, so that when they purchase a subscription and/or JamTracks, you will be paid a revenue share on their purchase payments per the terms of the affiliate agreement.
p.prompt
| Click “select link” next to the link you want to use, then copy the link to the clipboard and paste it wherever you want to share it.
table.links-table
thead

View File

@ -759,12 +759,13 @@ Rails.application.routes.draw do
match '/live_streams/bad_audio' => 'api_alerts#bad_audio', :via => :post # used by client; don't change route
# links generated to help affiliates share relevant links
get '/links/jamtrack_songs' => 'api_links#jamtrack_song_index'
get '/links/jamtrack_bands' => 'api_links#jamtrack_band_index'
get '/links/jamtrack_general' => 'api_links#jamtrack_general_index'
get '/links/jamkazam' => 'api_links#jamkazam_general_index'
get '/links/sessions' => 'api_links#session_index'
get '/links/recordings' => 'api_links#recording_index'
#get '/links/jamtrack_songs' => 'api_links#jamtrack_song_index'
#get '/links/jamtrack_bands' => 'api_links#jamtrack_band_index'
#get '/links/jamtrack_general' => 'api_links#jamtrack_general_index'
#get '/links/jamkazam' => 'api_links#jamkazam_general_index'
#get '/links/sessions' => 'api_links#session_index'
#get '/links/recordings' => 'api_links#recording_index'
get '/links/all' => 'api_links#all'
match '/lesson_sessions' => 'api_lesson_sessions#index', :via => :get
match '/lesson_bookings/unprocessed' => 'api_lesson_bookings#unprocessed', :via => :get