add tests for affiliate earnings

This commit is contained in:
Nuwan 2024-05-23 00:55:03 +05:30
parent bbe83a008f
commit 8868ff718f
17 changed files with 18613 additions and 10966 deletions

View File

@ -10,58 +10,7 @@ describe('User subscribe to a plan', () => {
email: 'sam@example.com'
});
cy.stubAuthenticate({ ...currentUser });
Cypress.on('window:before:load', win => {
win.gon = {
//session variables
global: {
subscription_codes: [
{
id: null,
name: 'Free',
price: 0,
cycle: 'month'
},
{
id: 'jamsubsilver',
name: 'Silver',
price: 4.99,
cycle: 'month'
},
{
id: 'jamsubgold',
name: 'Gold',
price: 9.99,
cycle: 'month'
},
{
id: 'jamsubplatinum',
name: 'Platinum',
price: 19.99,
cycle: 'month'
},
{
id: 'jamsubsilveryearly',
name: 'Silver',
price: 49.99,
cycle: 'year'
},
{
id: 'jamsubgoldyearly',
name: 'Gold',
price: 99.99,
cycle: 'year'
},
{
id: 'jamsubplatinumyearly',
name: 'Platinum',
price: 199.99,
cycle: 'year'
},
]
}
}
});
cy.stubGonSubscriptionCodes();
cy.intercept('GET', /\S+\/get_subscription/, {
@ -136,7 +85,7 @@ describe('User subscribe to a plan', () => {
});
cy.get('[data-testid=playtime]').within($playtime => {
cy.get('label').contains('You have unlimited play time');
cy.get('label').contains('You have unlimited play time.');
});
cy.get('[data-testid=subscription-explanation]').within($expl => {

View File

@ -0,0 +1,137 @@
/// <reference types="cypress" />
import makeFakeUser from '../../factories/user';
describe('Affiliate Earnings', () => {
beforeEach(() => {
const currentUser = makeFakeUser();
cy.stubAuthenticate({ id: currentUser.id });
cy.stubGonSubscriptionCodes();
});
describe('When user has no earnings', () => {
it('should show no earnings message', () => {
cy.intercept('GET', /\S+\/api\/affiliate_partners\/payments/, {
body: [
{
payments: [],
next: null
}
]
}).as('fetchAllEarnings');
cy.visit('/affiliate/earnings');
cy.wait('@fetchAllEarnings');
cy.contains('No Records!');
});
});
describe('When user has earnings', () => {
it('should show earnings', () => {
cy.intercept('GET', /\S+\/api\/affiliate_partners\/payments/, {
body: {
payments: [
{
payment_type: 'monthly',
year: 2021,
month: 9,
subscriptions: [
{
plan: 'jamsubsilver',
count: 2
}
],
jamtracks_sold: 3,
due_amount_in_cents: 5000
},
{
payment_type: 'monthly',
year: 2021,
month: 8,
subscriptions: [
{
plan: 'jamsubgold',
count: 1
},
{
plan: 'jamsubplatinum',
count: 2
}
],
jamtracks_sold: 2,
due_amount_in_cents: 3000
}
],
next: null
}
}).as('fetchAllEarnings');
cy.visit('/affiliate/earnings');
cy.wait('@fetchAllEarnings');
cy.get('[data-testid=affiliateEarningsList] tbody tr:first-child td')
.eq(0)
.should('have.text', 'September - 2021');
cy.get('[data-testid=affiliateEarningsList] tbody tr:first-child td')
.eq(1)
.should('have.text', 'Silver - 2');
cy.get('[data-testid=affiliateEarningsList] tbody tr:first-child td')
.eq(2)
.should('have.text', '3');
cy.get('[data-testid=affiliateEarningsList] tbody tr:first-child td')
.eq(3)
.should('have.text', '$50.00');
});
});
describe('in mobile', () => {
beforeEach(() => {
cy.viewport('iphone-6');
});
it('should show earnings', () => {
cy.intercept('GET', /\S+\/api\/affiliate_partners\/payments/, {
body: {
payments: [
{
payment_type: 'monthly',
year: 2021,
month: 9,
subscriptions: [
{
plan: 'jamsubsilver',
count: 2
}
],
jamtracks_sold: 3,
due_amount_in_cents: 5000
},
{
payment_type: 'monthly',
year: 2021,
month: 8,
subscriptions: [
{
plan: 'jamsubgold',
count: 1
},
{
plan: 'jamsubplatinum',
count: 2
}
],
jamtracks_sold: 2,
due_amount_in_cents: 3000
}
],
next: null
}
}).as('fetchAllEarningsOnMobile');
cy.visit('/affiliate/earnings');
cy.wait('@fetchAllEarningsOnMobile');
cy.contains('September 2021');
cy.contains('Silver - 2');
cy.contains('JamTracks Sold: 3');
cy.contains('Earnings: $50.00');
});
});
});

View File

@ -48,3 +48,58 @@ Cypress.Commands.add('stubUnauthenticate', () => {
body: {}
}).as('getUnauthenticateCurrentUser')
});
Cypress.Commands.add('stubGonSubscriptionCodes', () => {
Cypress.on('window:before:load', win => {
win.gon = {
//session variables
global: {
subscription_codes: [
{
id: null,
name: 'Free',
price: 0,
cycle: 'month'
},
{
id: 'jamsubsilver',
name: 'Silver',
price: 4.99,
cycle: 'month'
},
{
id: 'jamsubgold',
name: 'Gold',
price: 9.99,
cycle: 'month'
},
{
id: 'jamsubplatinum',
name: 'Platinum',
price: 19.99,
cycle: 'month'
},
{
id: 'jamsubsilveryearly',
name: 'Silver',
price: 49.99,
cycle: 'year'
},
{
id: 'jamsubgoldyearly',
name: 'Gold',
price: 99.99,
cycle: 'year'
},
{
id: 'jamsubplatinumyearly',
name: 'Platinum',
price: 199.99,
cycle: 'year'
},
]
}
}
});
});

27720
jam-ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,990 @@
import React from 'react';
const AgreementText = () => {
return (
<>
<div id="partner-agreement-v1">
<p className="c2">
<span className="c0">Updated: February 9, 2021.</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">This Affiliate Agreement (this &ldquo;</span>
<span className="c0 c10">Agreement</span>
<span className="c0">
&rdquo;) contains the terms and conditions that govern your participation in the JamKazam affiliate
marketing program (the &ldquo;
</span>
<span className="c0 c10">Program</span>
<span className="c0">&rdquo;). &ldquo;</span>
<span className="c0 c10">JamKazam</span>
<span className="c0">&rdquo;, &ldquo;</span>
<span className="c0 c10">we</span>
<span className="c0">,&rdquo; &ldquo;</span>
<span className="c0 c10">us</span>
<span className="c0">,&rdquo; or &ldquo;</span>
<span className="c0 c10">our</span>
<span className="c0">&rdquo; means JamKazam, Inc. &ldquo;</span>
<span className="c0 c10">You</span>
<span className="c0">&rdquo; or &ldquo;</span>
<span className="c0 c10">your</span>
<span className="c0">&rdquo; means the applicant. A &ldquo;</span>
<span className="c0 c10">site</span>
<span className="c0">&rdquo; means a website. &ldquo;</span>
<span className="c0 c10">JamKazam Site</span>
<span className="c0">
&rdquo; means the jamkazam.com website or a JamKazam applicaion or any other site that is owned or operated
by or on behalf of us and which is identified as participating in the Program in the{' '}
</span>
<span className="c0 c10 c17">Program Advertising Fee Schedule</span>
<span className="c0">&nbsp;in Section 10, as applicable. &ldquo;</span>
<span className="c0 c10">Your Site</span>
<span className="c0">
&rdquo; means any site(s), software application(s), or content that you create, own, or operate and link to
the JamKazam Site.{' '}
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">1. Description of the Program</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
The purpose of the Program is to permit you to advertise Products on Your Site and to earn advertising fees
for Qualifying Purchases (defined in Section 7) made by your Qualifying Customers (defined in Section 7). A
&ldquo;
</span>
<span className="c0 c10">Product</span>
<span className="c0">&rdquo; a product or service sold on the JamKazam Site and listed in the </span>
<span className="c0 c10 c17">Program Advertising Fee Schedule</span>
<span className="c0">
&nbsp;in Section 10. In order to facilitate your advertisement of Products, we may make available to you
data, images, text, link formats, widgets, links, and other linking tools, and other information in
connection with the Program (&ldquo;Content&rdquo;).{' '}
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">2. Enrollment, Suitability, and Communications</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
To enroll in the Program, you must execute this Agreement by clicking the &ldquo;I Agree&rdquo; button at
the end of this Agreement, after having thoroughly reviewed the Agreement.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
Your Site may be considered unsuitable for participation in the Program resulting in termination of this
Agreement by JamKazam if Your Site:
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<ul className="c6 lst-kix_list_47-0 start">
<li className="c3 c2">
<span className="c0 c8">promotes or contains sexually explicit materials;</span>
</li>
<li className="c3 c2">
<span className="c0 c8">promotes violence or contains violent materials;</span>
</li>
<li className="c3 c2">
<span className="c0 c8">promotes or contains libelous or defamatory materials;</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
promotes discrimination, or employs discriminatory practices, based on race, sex, religion, nationality,
disability, sexual orientation, or age;
</span>
</li>
<li className="c3 c2">
<span className="c0 c8">promotes or undertakes illegal activities;</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
is directed toward children under 13 years of age, as defined by the Children&rsquo;s Online Privacy
Protection Act (15 U.S.C. &sect;&sect; 6501-6506) and any regulations promulgated thereunder;
</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
includes any trademark of JamKazam or a variant or misspelling of a trademark of JamKazam in any domain
name, subdomain name, or in any username, group name, or other identifier on any social networking site;
or
</span>
</li>
<li className="c3 c2">
<span className="c0 c8">otherwise violates intellectual property rights.</span>
</li>
</ul>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
You will ensure that the information you provide in executing this Agreement and otherwise associated with
your account, including your name, email address, mailing address, tax ID, and other information, is at all
times complete, accurate, and up-to-date. We may send notifications (if any), reports (if any), and other
communications relating to the Program and this Agreement to the email address then-currently associated
with your JamKazam account. You will be deemed to have received all notifications, reports, and other
communications sent to that email address, even if the email address associated with your account is no
longer current. We may mail checks (if any) payable to you for advertising fees earned under this Agreement
to the mailing address then-currently associated with your JamKazam account. You will be deemed to have
received all checks sent to that mailing address, even if the mailing address associated with your account
is no longer current. If we send a check to a mailing address that is no longer valid, we may, in our sole
discretion, choose to issue a stop payment order for such a check and send a new check to a new current
address that your provide, but in such a case, we will charge a US$50.00 fee for this exception process.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
If you are a Non-US person participating in the Program, you agree that you will perform all services under
the Agreement outside the United States.{' '}
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">3. Links on Your Site</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
After you have entered into this Agreement, you may display Special Links on Your Site. &ldquo;
</span>
<span className="c0 c10">Special Links</span>
<span className="c0">
&rdquo; are links to the JamKazam Site that you place on Your Site in accordance with this Agreement, that
properly utilize the special &ldquo;tagged&rdquo; link formats we specify or provide. Special Links permit
accurate tracking, reporting, and accrual of advertising fees.{' '}
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
You may earn advertising fees only as described in Section 7 and only with respect to activity on the
JamKazam Site occurring directly through Special Links. We will have no obligation to pay you advertising
fees if you fail to properly format the links on Your Site to the JamKazam Site as Special Links, including
to the extent that such failure may result in any reduction of advertising fee amounts that would otherwise
be paid to you under this Agreement.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">4. Program Requirements</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">You hereby consent to us:</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<ul className="c6 lst-kix_list_44-0 start">
<li className="c3 c2">
<span className="c0 c8">sending you emails relating to the Program from time to time; and</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
monitoring, recording, using, and disclosing information about Your Site and visitors to Your Site that we
obtain in connection with your display of Special Links in accordance with the{' '}
</span>
<span className="c0 c10 c8 c17">JamKazam Privacy Policy</span>
<span className="c0 c8">.</span>
</li>
</ul>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">5. Responsibility for Your Site</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
You will be solely responsible for Your Site including its development, operation, and maintenance, and all
materials that appear on or within it. For example, you will be solely responsible for:
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<ul className="c6 lst-kix_list_45-0 start">
<li className="c3 c2">
<span className="c0 c8">the technical operation of Your Site and all related equipment;</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
displaying Special Links and Content on Your Site in compliance with this Agreement and any agreement
between you and any other person or entity (including any restrictions or requirements placed on you by
any person or entity that hosts Your Site);
</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
creating and posting, and ensuring the accuracy, completeness, and appropriateness of, materials posted on
Your Site (including all Product descriptions and other Product-related materials and any information you
include within or associate with Special Links);
</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
using the Content, Your Site, and the materials on or within Your Site in a manner that does not infringe,
violate, or misappropriate any of our rights or those of any other person or entity (including copyrights,
trademarks, privacy, publicity or other intellectual property or proprietary rights);
</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
disclosing on Your Site accurately and adequately, either through a privacy policy or otherwise, how you
collect, use, store, and disclose data collected from visitors, including, where applicable, that third
parties (including us and other advertisers) may serve content and advertisements, collect information
directly from visitors, and place or recognize cookies on visitors&rsquo; browsers; and
</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
any use that you make of the Content and the JamKazam Marks, whether or not permitted under this
Agreement.
</span>
</li>
</ul>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
We will have no liability for these matters or for any of your end users&rsquo; claims relating to these
matters, and you agree to defend, indemnify, and hold us, our affiliates and licensors, and our and their
respective employees, officers, directors, and representatives, harmless from and against all claims,
damages, losses, liabilities, costs, and expenses (including attorneys&rsquo; fees) relating to (a) Your
Site or any materials that appear on Your Site, including the combination of Your Site or those materials
with other applications, content, or processes; (b) the use, development, design, manufacture, production,
advertising, promotion, or marketing of Your Site or any materials that appear on or within Your Site, and
all other matters described in this Section 5; (c) your use of any Content, whether or not such use is
authorized by or violates this Agreement or any applicable law; (d) your violation of any term or condition
of this Agreement; or (e) your or your employees&#39; negligence or willful misconduct.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">6. Order Processing</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
We will process Product orders placed by Qualifying Customers (defined in Section 7) on the JamKazam Site.
We reserve the right to reject orders that do not comply with any requirements on the JamKazam Site, as they
may be updated from time to time. We will track Qualifying Purchases (defined in Section 7) for reporting
and advertising fee accrual purposes and will make available to you reports summarizing those Qualifying
Purchases.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">7. Advertising Fees</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
We will pay you advertising fees on Qualifying Purchases in accordance with Section 8 and the{' '}
</span>
<span className="c0 c10 c17">Program Advertising Fee Schedule</span>
<span className="c0">&nbsp;in Section 10. Subject to the exclusions set forth below, a &ldquo;</span>
<span className="c0 c10">Qualifying Purchase</span>
<span className="c0">
&rdquo; occurs when a Qualifying Customer purchases and pays for a Product within three (3) years of the
date on which such Qualifying Customer registered to create his/her JamKazam account. A &ldquo;
</span>
<span className="c0 c10">Qualifying Customer</span>
<span className="c0">
&rdquo; is an end user who: (a) clicks through a Special Link on Your Site to the JamKazam Site; and (b)
during the single Session created by this click through, registers to create a new JamKazam account. A
&ldquo;
</span>
<span className="c0 c10">Session</span>
<span className="c0">
&rdquo; begins when an end user clicks through a Special Link on Your Site to the JamKazam Site and ends
when such end user leaves the JamKazam Site.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
Qualifying Purchases exclude, and we will not pay advertising fees on any of, the following:
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<ul className="c6 lst-kix_list_46-0 start">
<li className="c3 c2">
<span className="c0 c8">
any Product purchase that is not correctly tracked or reported because the links from Your Site to the
JamKazam Site are not properly formatted;
</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
any Product purchased through a Special Link by you or on your behalf, including Products you purchase
through Special Links for yourself, friends, relatives, or associates;
</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
any Product purchased through a Special Link that violates the terms of this Agreement;
</span>
</li>
<li className="c2 c3">
<span className="c0 c8">any Product order that is canceled or returned; </span>
</li>
<li className="c3 c2">
<span className="c0 c8">any Product purchase that becomes classified as bad debt; or</span>
</li>
<li className="c3 c2">
<span className="c0 c8">
any Product purchase for which we process a promotional discount against the transaction that makes such
Product free.
</span>
</li>
</ul>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">8. Advertising Fee Payment</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
We will pay you advertising fees on a quarterly basis for Qualifying Purchases paid for in a given calendar
quarter, subject to any applicable withholding or deduction described below. We will pay you approximately
30 days following the end of each calendar quarter by mailing a check in the amount of the advertising fees
you earn to the mailing address then-currently associated with your JamKazam account, or by or processing a
digital funds transfer (e.g. PayPal) to an account you designate, but we may accrue and withhold payment of
advertising fees until the total amount due to you is at least US$25.00. If you do not have a valid mailing
address associated with your JamKazam account within 30 days of the end of a calendar quarter, we will
withhold any unpaid accrued advertising fees until you have associated a valid mailing address and notified
us that you have done so.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
Further, any unpaid accrued advertising fees in your account may be subject to escheatment under state law.
We may be obligated by law to obtain tax information from you if you are a U.S. citizen, U.S. resident, or
U.S. corporation, or if your business is otherwise taxable in the U.S. If we request tax information from
you and you do not provide it to us, we may (in addition to any other rights or remedies available to us)
withhold your advertising fees until you provide this information or otherwise satisfy us that you are not a
person from whom we are required to obtain tax information.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">9. Policies and Pricing</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
Qualifying Customers who buy Products through this Program are our customers with respect to all activities
they undertake in connection with the JamKazam Site. Accordingly, as between you and us, all pricing, terms
of sale, rules, policies, and operating procedures concerning customer orders, customer service, and product
sales set forth on the JamKazam Site will apply to such Qualifying Customers, and we may change them at any
time in our sole discretion.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">10. Program Advertising Fee Schedule</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
We will determine and calculate amounts payable to you as advertising fees for Qualifying Purchases as set
forth below (the &ldquo;
</span>
<span className="c0 c10">Program Advertising Fee Schedule</span>
<span className="c0">&rdquo;).</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<a href="#" name="fcab919a72a4bb6a7511330807c3ec6acfd219eb" />
<a href="#" name="0" />
<ul className="c6 lst-kix_list_46-0 start">
<li className="c3 c2">
<div className="c0 c8">Product: Silver, Gold, and Platinum Monthly or Annual Subscriptions</div>
<div className="c2">
JamKazam will pay advertising fees of 30% of the net revenues from Qualifying Purchases by Qualifying
Customers of these Products.
</div>
</li>
<li className="c3 c2">
<div className="c0 c8">Product: JamTracks</div>
<div className="c2">
JamKazam will pay US$0.25 per JamTrack sold as a Qualifying Purchase by Qualifying Customers of these
Products.
</div>
</li>
</ul>
{/* <p className="c2 c5"><span className="c0"></span></p>
<a href="#" name="fcab919a72a4bb6a7511330807c3ec6acfd219eb"></a><a href="#" name="0"></a>
<table cellpadding="0" cellspacing="0" className="c16">
<tbody>
<tr className="c19">
<td className="c1" colspan="1" rowspan="1"><p className="c4 c2"><span className="c12 c0 c7">Product</span></p></td>
<td className="c20" colspan="1" rowspan="1"><p className="c4 c2"><span className="c0 c7 c12">Advertising Fees</span></p></td>
</tr>
<tr className="c19">
<td className="c1" colspan="1" rowspan="1"><p className="c4 c2"><span className="c12 c0 c8">JamTracks</span></p></td>
<td className="c20" colspan="1" rowspan="1"><p className="c2 c4">
<span className="c12 c0 c8">We will pay advertising fees of US$0.20 per JamTrack sold as a Qualifying Purchase.</span>
</p></td>
</tr>
</tbody>
</table> */}
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
From time to time, we may modify this Program Advertising Fee Schedule as part of modifications made to this
Agreement.
</span>
</p>
<p className="c2 c5">
<span className="c0 c7 c13" />
</p>
<p className="c2">
<span className="c0 c7">11. Limited License</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
Subject to the terms of this Agreement and solely for the limited purposes of advertising Products on, and
directing end users to, the JamKazam Site in connection with the Program, we hereby grant you a limited,
revocable, non-transferable, non-sublicensable, non-exclusive, royalty-free license to (a) copy and display
the Content solely on Your Site; and (b) use only those of our trademarks and logos that we may make
available to you as part of Content (those trademarks and logos, collectively, &ldquo;
</span>
<span className="c0 c10">JamKazam Marks</span>
<span className="c0">&rdquo;) solely on Your Site.</span>
</p>
<p className="c2 c5">
<a name="h.gjdgxs" />
</p>
<p className="c2">
<span className="c0">
The license set forth in this Section 11 will immediately and automatically terminate if at any time you do
not timely comply with any obligation under this Agreement, or otherwise upon termination of this Agreement.
In addition, we may terminate the license set forth in this Section 11 in whole or in part upon written
notice to you. You will promptly remove from Your Site and delete or otherwise destroy all of the Content
and JamKazam Marks with respect to which the license set forth in this Section 11 is terminated or as we may
otherwise request from time to time.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">12. Reservation of Rights; Submissions</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
Other than the limited licenses expressly set forth in Section 11, we reserve all right, title and interest
(including all intellectual property and proprietary rights) in and to, and you do not, by virtue of this
Agreement or otherwise, acquire any ownership interest or rights in or to, the Program, Special Links,
Content, Products, any domain name owned or operated by us, our trademarks and logos (including the JamKazam
Marks), and any other intellectual property and technology that we provide or use in connection with the
Program (including any application program interfaces, software development kits, libraries, sample code,
and related materials). If you provide us with suggestions, reviews, modifications, data, images, text, or
other information or content about a Product or in connection with this Agreement, any Content, or your
participation in the Program, or if you modify any Content in any way, (collectively, &ldquo;
</span>
<span className="c0 c10">Your Submission</span>
<span className="c0">
&rdquo;), you hereby irrevocably assign to us all right, title, and interest in and to Your Submission and
grant us (even if you have designated Your Submission as confidential) a perpetual, paid-up royalty-free,
nonexclusive, worldwide, irrevocable, freely transferable right and license to (a) use, reproduce, perform,
display, and distribute Your Submission in any manner; (b) adapt, modify, re-format, and create derivative
works of Your Submission for any purpose; (c) use and publish your name in the form of a credit in
conjunction with Your Submission (however, we will not have any obligation to do so); and (d) sublicense the
foregoing rights to any other person or entity. Additionally, you hereby warrant that: (y) Your Submission
is your original work, or you obtained Your Submission in a lawful manner; and (z) our and our
sublicensees&rsquo; exercise of rights under the license above will not violate any person&rsquo;s or
entity&rsquo;s rights, including any copyright rights. You agree to provide us such assistance as we may
require to document, perfect, or maintain our rights in and to Your Submission.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">13. Compliance with Laws</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
In connection with your participation in the Program you will comply with all applicable laws, ordinances,
rules, regulations, orders, licenses, permits, judgments, decisions, and other requirements of any
governmental authority that has jurisdiction over you, including laws (federal, state, or otherwise) that
govern marketing email (e.g., the CAN-SPAM Act of 2003).
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">14. Term and Termination</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
The term of this Agreement will begin upon your execution of this Agreement by clicking the &ldquo;I
Agree&rdquo; button at the end of this Agreement, only if such execution is processed and confirmed
successfully by the JamKazam Site, and will end when terminated by either you or us. Either you or we may
terminate this Agreement at any time, with or without cause, by giving the other party written notice of
termination. Upon any termination of this Agreement, any and all licenses you have with respect to Content
will automatically terminate, and you will immediately stop using the Content and JamKazam Marks and
promptly remove from Your Site and delete or otherwise destroy all links to the JamKazam Site, all JamKazam
Marks, all other Content, and any other materials provided or made available by or on behalf of us to you
under this Agreement or otherwise in connection with the Program. We may withhold accrued unpaid advertising
fees for a reasonable period of time following termination to ensure that the correct amount is paid (e.g.,
to account for any cancelations or returns). Upon any termination of this Agreement, all rights and
obligations of the parties will be extinguished, except that the rights and obligations of the parties under
Sections 5, 9, 12, 13, 14, 16, 17, 18, 19, and 20, together with any of our payment obligations that accrue
in accordance with Sections 6, 7, 8, and 10 following the termination of this Agreement, will survive the
termination of this Agreement. No termination of this Agreement will relieve either party for any liability
for any breach of, or liability accruing under, this Agreement prior to termination.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">15. Modification</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
We may modify any of the terms and conditions contained in this Agreement at any time and in our sole
discretion by posting a change notice or revised agreement on the JamKazam Site or by sending notice of such
modification to you by email to the email address then-currently associated with your JamKazam account (any
such change by email will be effective on the date specified in such email and will in no event be less than
two business days after the date the email is sent). Modifications may include, for example, changes to the
Program Advertising Fee Schedule, payment procedures, and other Program requirements. IF ANY MODIFICATION IS
UNACCEPTABLE TO YOU, YOUR ONLY RECOURSE IS TO TERMINATE THIS AGREEMENT. YOUR CONTINUED PARTICIPATION IN THE
PROGRAM FOLLOWING THE EFFECTIVE DATE OF ANY MODIFICATION (E.G., THE DATE OF OUR POSTING OF A CHANGE NOTICE
OR REVISED AGREEMENT ON THE JAMKAZAM SITE OR THE DATE SPECIFIED IN ANY EMAIL TO YOU REGARDING SUCH
MODIFICATION) WILL CONSTITUTE YOUR BINDING ACCEPTANCE OF THE CHANGE.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">16. Relationship of Parties</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
You and we are independent contractors, and nothing in this Agreement will create any partnership, joint
venture, agency, franchise, sales representative, or employment relationship between you and us or our
respective affiliates. You will have no authority to make or accept any offers or representations on our
behalf. You will not make any statement, whether on Your Site or otherwise, that contradicts or may
contradict anything in this section. If you authorize, assist, encourage, or facilitate another person or
entity to take any action related to the subject matter of this Agreement, you will be deemed to have taken
the action yourself.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">17. Limitation of Liability</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
WE WILL NOT BE LIABLE FOR INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR EXEMPLARY DAMAGES (INCLUDING ANY
LOSS OF REVENUE, PROFITS, GOODWILL, USE, OR DATA) ARISING IN CONNECTION WITH THIS AGREEMENT, THE PROGRAM,
THE JAMKAZAM SITE, OR THE SERVICE OFFERINGS (DEFINED BELOW), EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY
OF THOSE DAMAGES. FURTHER, OUR AGGREGATE LIABILITY ARISING IN CONNECTION WITH THIS AGREEMENT, THE PROGRAM,
THE JAMKAZAM SITE, AND THE SERVICE OFFERINGS WILL NOT EXCEED THE TOTAL ADVERTISING FEES PAID OR PAYABLE TO
YOU UNDER THIS AGREEMENT IN THE TWELVE MONTHS IMMEDIATELY PRECEDING THE DATE ON WHICH THE EVENT GIVING RISE
TO THE MOST RECENT CLAIM OF LIABILITY OCCURRED.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">18. Disclaimers</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
THE PROGRAM, THE JAMKAZAM SITE, ANY PRODUCTS AND SERVICES OFFERED ON THE JAMKAZAM SITE, ANY SPECIAL LINKS,
LINK FORMATS, CONTENT, JAMKAZAM.COM DOMAIN NAME, OUR TRADEMARKS AND LOGOS (INCLUDING THE JAMKAZAM MARKS),
AND ALL TECHNOLOGY, SOFTWARE, FUNCTIONS, MATERIALS, DATA, IMAGES, TEXT, AND OTHER INFORMATION AND CONTENT
PROVIDED OR USED BY OR ON BEHALF OF US OR LICENSORS IN CONNECTION WITH THE PROGRAM (COLLECTIVELY THE
&ldquo;SERVICE OFFERINGS&rdquo;) ARE PROVIDED &ldquo;AS IS.&rdquo; NEITHER WE NOR ANY OF OUR LICENSORS MAKE
ANY REPRESENTATION OR WARRANTY OF ANY KIND, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT
TO THE SERVICE OFFERINGS. EXCEPT TO THE EXTENT PROHIBITED BY APPLICABLE LAW, WE AND OUR LICENSORS DISCLAIM
ALL WARRANTIES WITH RESPECT TO THE SERVICE OFFERINGS, INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY,
SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND QUIET ENJOYMENT, AND ANY
WARRANTIES ARISING OUT OF ANY COURSE OF DEALING, PERFORMANCE, OR TRADE USAGE. WE MAY DISCONTINUE ANY SERVICE
OFFERING, OR MAY CHANGE THE NATURE, FEATURES, FUNCTIONS, SCOPE, OR OPERATION OF ANY SERVICE OFFERING, AT ANY
TIME AND FROM TIME TO TIME. NEITHER WE NOR ANY OF OUR LICENSORS WARRANT THAT THE SERVICE OFFERINGS WILL
CONTINUE TO BE PROVIDED, WILL FUNCTION AS DESCRIBED, CONSISTENTLY OR IN ANY PARTICULAR MANNER, OR WILL BE
UNINTERRUPTED, ACCURATE, ERROR FREE, OR FREE OF HARMFUL COMPONENTS. NEITHER WE NOR ANY OF OUR LICENSORS WILL
BE RESPONSIBLE FOR (A) ANY ERRORS, INACCURACIES, OR SERVICE INTERRUPTIONS, INCLUDING POWER OUTAGES OR SYSTEM
FAILURES; OR (B) ANY UNAUTHORIZED ACCESS TO OR ALTERATION OF, OR DELETION, DESTRUCTION, DAMAGE, OR LOSS OF,
YOUR SITE OR ANY DATA, IMAGES, TEXT, OR OTHER INFORMATION OR CONTENT. NO ADVICE OR INFORMATION OBTAINED BY
YOU FROM US OR FROM ANY OTHER PERSON OR ENTITY OR THROUGH THE PROGRAM, CONTENT, OR THE JAMKAZAM SITE WILL
CREATE ANY WARRANTY NOT EXPRESSLY STATED IN THIS AGREEMENT. FURTHER, NEITHER WE NOR ANY OF OUR LICENSORS
WILL BE RESPONSIBLE FOR ANY COMPENSATION, REIMBURSEMENT, OR DAMAGES ARISING IN CONNECTION WITH (X) ANY LOSS
OF PROSPECTIVE PROFITS OR REVENUE, ANTICIPATED SALES, GOODWILL, OR OTHER BENEFITS, (Y) ANY INVESTMENTS,
EXPENDITURES, OR COMMITMENTS BY YOU IN CONNECTION WITH THIS AGREEMENT OR YOUR PARTICIPATION IN THE PROGRAM,
OR (Z) ANY TERMINATION OF THIS AGREEMENT OR YOUR PARTICIPATION IN THE PROGRAM.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">19. Disputes</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
Any dispute relating in any way to the Program or this Agreement will be resolved by binding arbitration,
rather than in court, except that you may assert claims in small claims court if your claims qualify. The
Federal Arbitration Act and federal arbitration law and the laws of the state of Texas, without regard to
principles of conflict of laws, will govern this Agreement and any dispute of any sort that might arise
between you and us.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
There is no judge or jury in arbitration, and court review of an arbitration award is limited. However, an
arbitrator can award on an individual basis the same damages and relief as a court (including injunctive and
declaratory relief or statutory damages), and must follow the terms of this Agreement as a court would.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
To begin an arbitration proceeding, you must send a letter requesting arbitration and describing your claim
to us at: JamKazam, Inc., Attn: Legal Department, 3924 Knollwood Drive, Austin TX 78731. The arbitration
will be conducted by the American Arbitration Association (&ldquo;AAA&rdquo;) under its rules, including the
AAA&rsquo;s Supplementary Procedures for Consumer-Related Disputes. The AAA&rsquo;s rules are available at
www.adr.org or by calling 1-800-778-7879. Payment of all filing, administration and arbitrator fees will be
governed by the AAA&rsquo;s rules. We will reimburse those fees for claims totaling less than $10,000 unless
the arbitrator determines the claims are frivolous. Likewise, we will not seek attorneys&rsquo; fees and
costs in arbitration unless the arbitrator determines the claims are frivolous. You may choose to have the
arbitration conducted by telephone, based on written submissions, or in person in the county where you live
or at another mutually agreed location.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
We each agree that any dispute resolution proceedings will be conducted only on an individual basis and not
in a class, consolidated or representative action. If for any reason a claim proceeds in court rather than
in arbitration, we each waive any right to a jury trial. We also both agree that you or we may bring suit in
court to enjoin infringement or other misuse of intellectual property rights.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
Notwithstanding anything to the contrary in this Agreement, we may seek injunctive or other relief in any
state, federal, or national court of competent jurisdiction for any actual or alleged infringement of our or
any other person or entity&rsquo;s intellectual property or proprietary rights. You further acknowledge and
agree that our rights in the Content are of a special, unique, extraordinary character, giving them peculiar
value, the loss of which cannot be readily estimated or adequately compensated for in monetary damages.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0 c7">20. Miscellaneous</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
You acknowledge and agree that we may at any time (directly or indirectly) solicit customer referrals on
terms that may differ from those contained in this Agreement or operate sites that are similar to or compete
with Your Site. You may not assign this Agreement, by operation of law or otherwise, without our express
prior written approval. Subject to that restriction, this Agreement will be binding on, inure to the benefit
of, and be enforceable against the parties and their respective successors and assigns. Our failure to
enforce your strict performance of any provision of this Agreement will not constitute a waiver of our right
to subsequently enforce such provision or any other provision of this Agreement. Whenever used in this
Agreement, the terms &ldquo;include(s),&rdquo; &ldquo;including,&rdquo; &ldquo;e.g.,&rdquo; and &ldquo;for
example&rdquo; mean, respectively, &ldquo;include(s), without limitation,&rdquo; &ldquo;including, without
limitation,&rdquo; &ldquo;e.g., without limitation,&rdquo; and &ldquo;for example, without
limitation.&rdquo; Any determinations or updates that may be made by us, any actions that may be taken by
us, and any approvals that may be given by us under this Agreement, may be made, taken, or given in our sole
discretion.
</span>
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2 c5">
<span className="c0" />
</p>
<p className="c2">
<span className="c0">
BY CLICKING THE &quot;I AGREE&quot; BUTTON BELOW, YOU AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT,
OR BY CONTINUING TO PARTICIPATE IN THE PROGRAM FOLLOWING OUR POSTING OF A CHANGE NOTICE OR REVISED AGREEMENT
ON THE JAMKAZAM.COM SITE, YOU (A) AGREE TO BE BOUND BY THIS AGREEMENT; (B) ACKNOWLEDGE AND AGREE THAT YOU
HAVE INDEPENDENTLY EVALUATED THE DESIRABILITY OF PARTICIPATING IN THE PROGRAM AND ARE NOT RELYING ON ANY
REPRESENTATION, GUARANTEE, OR STATEMENT OTHER THAN AS EXPRESSLY SET FORTH IN THIS AGREEMENT; AND (C) HEREBY
REPRESENT AND WARRANT THAT YOU ARE LAWFULLY ABLE TO ENTER INTO CONTRACTS (E.G., YOU ARE NOT A MINOR) AND
THAT YOU ARE AND WILL REMAIN IN COMPLIANCE WITH THIS AGREEMENT. IN ADDITION, IF THIS AGREEMENT IS BEING
AGREED TO BY A COMPANY OR OTHER LEGAL ENTITY, THEN THE PERSON AGREEING TO THIS AGREEMENT ON BEHALF OF THAT
COMPANY OR ENTITY HEREBY REPRESENTS AND WARRANTS THAT HE OR SHE IS AUTHORIZED AND LAWFULLY ABLE TO BIND THAT
COMPANY OR ENTITY TO THIS AGREEMENT.
</span>
</p>
{/* <p className="c2 c5"><span className="c0"></span></p>
<p className="c2"><span className="c0">Radio buttons for:</span></p>
<ul className="c6 lst-kix_list_43-0 start">
<li className="c3 c2"><span className="c0 c8">I am entering into this Agreement as an individual</span></li>
<li className="c3 c2">
<span className="c0 c8">I am executing this Agreement on behalf of the company or entity listed below</span></li>
</ul>
<p className="c2 c5"><span className="c0"></span></p>
<p className="c2">
<span className="c0">If the user clicks the entity radio button above, we display an Entity Name label and associated text entry box, and an Entity Type label with a drop down list box with the following options: Sole Proprietorship, Limited Liability Company (LLC), S Corporation, C Corporation, Partnership, Trust/Estate, and Other.</span>
</p>
<p className="c2 c5"><span className="c0"></span></p>
<p className="c2">
<span className="c0">Green &ldquo;I Agree&rdquo; button on the left / centered. Red &ldquo;I Do Not Agree&rdquo; button on the right / centered.</span>
</p>
<p className="c2 c5"><span className="c0"></span></p>
<p className="c2 c5"><span className="c0"></span></p>
<p className="c2 c5"><span></span></p>
*/}
<div>
<p className="c2 c21">
<span className="c8 c14">JamKazam Confidential&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span className="c14 c8">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;02/09/2021</span>
</p>
</div>
</div>
</>
);
};
export default AgreementText;

View File

@ -0,0 +1,249 @@
import React, { useRef, useState, useEffect } from 'react';
import { Card, CardBody, Label, Form, FormGroup, Input, Row, Col } from 'reactstrap';
import Select from 'react-select';
import FalconCardHeader from '../common/FalconCardHeader';
import { useTranslation } from 'react-i18next';
import { Scrollbar } from 'react-scrollbars-custom';
import AgreementText from './AgreementText';
import { formatDate } from '../../helpers/utils';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { createAffiliatePartner, getAffiliatePartnerData } from '../../helpers/rest';
import { toast } from 'react-toastify';
import { useAuth } from '../../context/UserAuth';
const JKAffiliateAgreement = () => {
const { t } = useTranslation('affiliate');
const { currentUser } = useAuth();
const scrollbar = useRef();
const [agreementData, setAgreementData] = useState(null);
const [hasExecuted, setHasExecuted] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [submitable, setSubmitable] = useState(false);
const [agreementType, setAgreementType] = useState('');
const [entityName, setEntityName] = useState('');
const [entityType, setEntityType] = useState('');
const [entityNameError, setEntityNameError] = useState('');
const [entityTypeError, setEntityTypeError] = useState('');
const ENTITY_TYPES = [
{
value: 'Sole Proprietor',
label: 'Sole Proprietor'
},
{
value: 'Limited Liability Company (LLC)',
label: 'Limited Liability Company (LLC)'
},
{
value: 'Partnership',
label: 'Partnership'
},
{
value: 'Trust State',
label: 'Trust State'
},
{
value: 'C Corporation',
label: 'C Corporation'
},
{
value: 'S Corporation',
label: 'S Corporation'
},
{
value: 'Other',
label: 'Other'
}
];
useEffect(() => {
if (!currentUser) {
return;
}
fetchAffiliate();
}, [currentUser]);
useEffect(() => {
if (agreementType === 'Individual') {
setSubmitable(true);
}else if (agreementType === 'Entity') {
if (entityName && entityType) {
setSubmitable(true);
} else {
setSubmitable(false);
}
}
}, [agreementType, entityName, entityType]);
const fetchAffiliate = async () => {
try{
const response = await getAffiliatePartnerData(currentUser.id);
const affiliate = await response.json();
if (affiliate) {
setAgreementData(affiliate.agreement);
if (affiliate.agreement && affiliate.agreement.signed_at) {
setHasExecuted(true);
}
}
}catch(error){
console.log(error);
}
}
const handleSubmit = e => {
e.preventDefault();
let entityTypeVal = "Individual"
if(agreementType !== 'Individual'){
if(!isEntityValid()){
setSubmitting(false);
return;
}
entityTypeVal = typeof entityType === 'object' ? entityType.value : entityType;
}
const params = {
partner_name: entityName,
entity_type: entityTypeVal
};
setSubmitting(true);
createAffiliatePartner(params).then(resp => {
console.log('resp', resp);
if (resp.ok) {
fetchAffiliate();
toast.success('Agreement executed successfully');
}
}).catch(err => {
console.log('err', err);
toast.error('Error executing agreement');
}).finally(() => {
setSubmitting(false);
});
};
const isEntityValid = () => {
setEntityNameError('');
setEntityTypeError('');
let isValid = true;
if (!entityName || entityName === '') {
setEntityNameError('Entity name is required');
isValid = false;
}
if (!entityType || entityType === '') {
setEntityTypeError('Entity type is required');
isValid = false;
}
return isValid;
}
return (
<div className="mx-auto" style={{ width: '80%' }}>
<Card>
<FalconCardHeader title={t('agreement.page_title')} titleClass="font-weight-semi-bold" />
<CardBody className="pt-3 pb-5">
{hasExecuted && (
<div className="alert alert-info execution-date">
({t('agreement.executed_on')} {formatDate(agreementData.signed_at)})
</div>
)}
<Scrollbar ref={scrollbar} style={{ width: '100%', height: 400 }} mobileNative={true}>
<AgreementText />
</Scrollbar>
</CardBody>
</Card>
{!hasExecuted && (
<>
<div className="mt-3">
<p>
<strong>
{t('agreement.agreement_note')}
</strong>
</p>
</div>
<Form onSubmit={handleSubmit}>
<FormGroup>
<Label for="agreement_individual" className='ml-3'>
<Input
type="radio"
name="agreement"
id="agreement_individual"
value="Individual"
onClick={e => {
setAgreementType(e.target.value);
setEntityName('');
setEntityType('');
setEntityNameError('');
setEntityTypeError('');
}}
/>
{t('agreement.form.individual')}
</Label>
</FormGroup>
<FormGroup>
<Label for="agreement_entity" className='ml-3'>
<Input
type="radio"
name="agreement"
id="agreement_entity"
value="Entity"
onClick={e => setAgreementType(e.target.value)}
/>
{t('agreement.form.entity')}
</Label>
</FormGroup>
{agreementType === 'Entity' && (
<>
<Row>
<Col md={6}>
<FormGroup>
<Label for="agreement_entity_name">{t('agreement.form.entity_name')}</Label>
<Input
type="text"
name="entity_name"
id="agreement_entity_name"
onChange={e => setEntityName(e.target.value)}
/>
{entityNameError && <div className="text-danger"><small>{entityNameError}</small></div>}
</FormGroup>
</Col>
</Row>
<Row>
<Col md={6}>
<FormGroup>
<Label for="agreement_entity_type">{t('agreement.form.entity_type')}</Label>
<Select
options={ENTITY_TYPES}
name="entity_type"
id="agreement_entity_type"
onChange={setEntityType}
/>
{entityTypeError && <div className="text-danger"><small>{entityTypeError}</small></div>}
</FormGroup>
</Col>
</Row>
</>
)}
<div className="d-flex align-content-center justify-content-start">
<input
type="submit"
formNoValidate
className="btn btn-primary"
value={t('agreement.form.submit')}
disabled={submitting || !submitable}
/>
<span className="ml-2">{submitting && <FontAwesomeIcon icon="spinner" />}</span>
</div>
</Form>
</>
)}
</div>
);
};
export default JKAffiliateAgreement;

View File

@ -0,0 +1,58 @@
import React from 'react';
import { useResponsive } from '@farfetch/react-context-responsive';
import { monthName } from '../../helpers/utils';
import { getDisplayName } from '../../helpers/subscriptionHelper';
import { Row, Col } from 'reactstrap';
const JKAffiliateEarning = ({ payment }) => {
const { greaterThan } = useResponsive();
return (
<>
{greaterThan.sm ? (
<tr>
<td>
{monthName(payment.month - 1)} - {payment.year}
</td>
<td>
{payment.subscriptions.map((subscription, index) => (
<div key={index}>
{getDisplayName(subscription.plan)} - {subscription.count}
</div>
))}
</td>
<td>{payment.jamtracks_sold}</td>
<td>${(payment.due_amount_in_cents / 100).toFixed(2)}</td>
</tr>
) : (
<>
<Row className='mb-2'>
<Col>
{monthName(payment.month - 1)} {payment.year}
</Col>
</Row>
<Row className='mb-3'>
<Col>
<div>
<strong><small>Subscriptions Sold:</small></strong>
</div>
{payment.subscriptions.map((subscription, index) => (
<div key={index}>
<small>{getDisplayName(subscription.plan)} - {subscription.count}</small>
</div>
))}
</Col>
</Row>
<Row className='mb-2'>
<Col>
<small>JamTracks Sold: {payment.jamtracks_sold}</small></Col>
</Row>
<Row>
<Col><small>Earnings: ${(payment.due_amount_in_cents / 100).toFixed(2)}</small></Col>
</Row>
</>
)}
</>
);
};
export default JKAffiliateEarning;

View File

@ -1,64 +1,69 @@
import React, { useState, useEffect } from 'react';
import { Card, CardBody, Table } from 'reactstrap';
import { Card, CardBody, Row, Col, Alert } from 'reactstrap';
import FalconCardHeader from '../common/FalconCardHeader';
import { useTranslation } from 'react-i18next';
import { getAffiliatePayments } from '../../helpers/rest';
import { monthName } from '../../helpers/utils';
import { getDisplayName } from '../../helpers/subscriptionHelper';
import { isIterableArray } from '../../helpers/utils';
import { useResponsive } from '@farfetch/react-context-responsive';
import JKAffiliateEarningsList from './JKAffiliateEarningsList';
import JKAffiliateEarningsSwiper from './JKAffiliateEarningsSwiper';
import Loader from '../common/Loader';
const JKAffiliateEarnings = () => {
const { t } = useTranslation('affiliate');
const [payments, setPayments] = useState([]);
const [loading, setLoading] = useState(false);
const { greaterThan } = useResponsive();
useEffect(() => {
setLoading(true);
getAffiliatePayments().then(resp => {
return resp.json();
}).then(data => {
console.log('payments', data);
setPayments(data.payments);
}).finally(() => {
setLoading(false);
});
getAffiliatePayments()
.then(resp => {
return resp.json();
})
.then(data => {
setPayments(data.payments);
})
.catch(err => {
console.error(err);
})
.finally(() => {
setLoading(false);
});
}, []);
return (
<Card style={{ width: '50%' }} className="mx-auto affiliate-links">
<Card style={{ width: '70%' }} className="mx-auto affiliate-links">
<FalconCardHeader title={t('earnings.page_title')} titleClass="font-weight-semi-bold" />
<CardBody className="pt-3 pb-5">
{payments && payments.length === 0 && !loading ? (
<p>{t('earnings.no_data')}</p>
) : payments && payments.length > 0 && (
<Table className="table table-sm table-borderless">
<thead>
<tr>
<th>{t('earnings.list.header.date')}</th>
<th>{t('earnings.list.header.subscriptions')}</th>
<th>{t('earnings.list.header.jamtracks')}</th>
<th>{t('earnings.list.header.earnings')}</th>
</tr>
</thead>
<tbody>
{payments.map((payment, index) => (
<tr key={`${payment.year}-${payment.month}`}>
<td>{monthName(payment.month-1)} - {payment.year}</td>
<td>
{payment.subscriptions.map((subscription, index) => (
<div key={index}>
{getDisplayName(subscription.plan)} - {subscription.count}
</div>
))}
</td>
<td>{payment.jamtracks_sold}</td>
<td>${(payment.due_amount_in_cents/100).toFixed(2)}</td>
</tr>
))}
</tbody>
</Table>
{loading || (payments && payments.length === 0) ? (
<Loader />
) : isIterableArray(payments) ? (
<>
{greaterThan.sm ? (
<Row className="mb-3 justify-content-between d-none d-md-block">
<div className="table-responsive-xl px-2">
<JKAffiliateEarningsList payments={payments} />
</div>
</Row>
) : (
<Row className="swiper-container d-block d-md-none" data-testid="sessionsSwiper">
<JKAffiliateEarningsSwiper payments={payments} />
</Row>
)}
</>
) : (
<Row className="p-card">
<Col>
<Alert color="info" className="mb-0">
{t('no_records', { ns: 'common' })}
</Alert>
</Col>
</Row>
)}
</CardBody>
</CardBody>
</Card>
);
};

View File

@ -0,0 +1,27 @@
import React from 'react';
import { Table } from 'reactstrap';
import JKAffiliateEarning from './JKAffiliateEarning';
import { useTranslation } from 'react-i18next';
const JKAffiliateEarningsList = ({ payments }) => {
const { t } = useTranslation('affiliate');
return (
<Table striped bordered className="fs--1" data-testid="affiliateEarningsList">
<thead>
<tr>
<th>{t('earnings.list.header.date')}</th>
<th>{t('earnings.list.header.subscriptions')}</th>
<th>{t('earnings.list.header.jamtracks')}</th>
<th>{t('earnings.list.header.earnings')}</th>
</tr>
</thead>
<tbody>
{payments.map((payment, index) => (
<JKAffiliateEarning key={`${payment.year}-${payment.month}`} payment={payment} />
))}
</tbody>
</Table>
);
};
export default JKAffiliateEarningsList;

View File

@ -0,0 +1,70 @@
import React from 'react';
import PropTypes from 'prop-types';
// import Swiper core and required modules
import SwiperCore, { Navigation, Pagination, Scrollbar, A11y } from 'swiper';
// Import Swiper React components
import { Swiper, SwiperSlide } from 'swiper/react';
import JKAffiliateEarning from './JKAffiliateEarning';
// Import Swiper styles
import 'swiper/swiper.scss';
import 'swiper/components/navigation/navigation.scss';
import 'swiper/components/pagination/pagination.scss';
import 'swiper/components/scrollbar/scrollbar.scss';
import { Card, CardBody, CardHeader } from 'reactstrap';
SwiperCore.use([Navigation, Pagination, Scrollbar, A11y]);
const JKAffiliateEarningsSwiper = ({ payments }) => {
return (
<>
<Swiper
spaceBetween={0}
slidesPerView={1}
//onSlideChange={() => console.log('slide change')}
onSlideNextTransitionEnd={swiper => {
if(swiper.isEnd){
//goNextPage()
}
}}
pagination={{
clickable: true,
type: 'custom'
}}
navigation={{
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
}}
>
{payments.map((payment, index) => (
<SwiperSlide key={`${payment.year}-${payment.month}-mobile`}>
<Card className="swiper-card">
<CardHeader className="text-center bg-200">
<h4 className="d-inline-block align-center mt-1">Payment</h4>
</CardHeader>
<CardBody>
<JKAffiliateEarning payment={payment} />
</CardBody>
</Card>
</SwiperSlide>
))}
</Swiper>
<div className="py-4 px-6 bg-white border-top w-100 fixed-bottom">
<div className="swiper-pagination" />
<div className="swiper-button-prev" />
<div className="swiper-button-next" />
</div>
</>
);
};
JKAffiliateEarningsSwiper.propTypes = {
payments: PropTypes.arrayOf(PropTypes.instanceOf(Object)).isRequired,
};
export default JKAffiliateEarningsSwiper;

View File

@ -3,24 +3,34 @@ import { Card, CardBody } from 'reactstrap';
import FalconCardHeader from '../common/FalconCardHeader';
import { useTranslation } from 'react-i18next';
import JKJamTracksAutoComplete from '../jamtracks/JKJamTracksAutoComplete';
import { getJamTracks } from '../../helpers/rest';
import { useFetchAffiliate } from '../../hooks/useFetchAffiliate';
import { getJamTracks, getAffiliatePartnerData } from '../../helpers/rest';
import { useAuth } from '../../context/UserAuth';
import { useHistory } from "react-router-dom";
const JKAffiliateLinks = () => {
const { t } = useTranslation('affiliate');
const { currentUser } = useAuth();
const history = useHistory();
const [affiliatePartnerId, setAffiliatePartnerId] = useState('xxxxx');
const [affiliate, setAffiliate] = useState(null);
const [jamTracks, setJamTracks] = useState([]);
const { fetchAffiliate } = useFetchAffiliate();
useEffect(() => {
fetchAffiliate().then(affiliate => {
console.log('affiliate', affiliate);
fetchAffiliate();
}, []);
const fetchAffiliate = async () => {
try{
const response = await getAffiliatePartnerData(currentUser.id);
const affiliate = await response.json();
if (affiliate) {
setAffiliate(affiliate);
setAffiliatePartnerId(affiliate.account.partner_id);
}
});
}, []);
}catch(error){
console.log(error);
}
}
const handleOnSelect = jamTrack => {
console.log('onSelect', jamTrack);
@ -36,7 +46,6 @@ const JKAffiliateLinks = () => {
} else {
options.song = selected.name;
}
console.log('options', options);
try {
const resp = await getJamTracks(options);
const data = await resp.json();
@ -45,6 +54,27 @@ const JKAffiliateLinks = () => {
} catch (error) {}
};
const homePageLink = `https://www.jamkazam.com?affiliate=${affiliatePartnerId}`;
const handleClickOnHomePageLink = () => {
if(affiliate){
window.open(homePageLink, '_blank');
}else{
history.push('/affiliate/agreement');
}
}
const jamTrackLink = jamTrack => {
return `https://jamkazam.com/jamtrack/landing/${jamTrack.plan_code}?affiliate=${affiliatePartnerId}`;
}
const handleOnClickJamTrackLink = () => {
if(affiliate){
window.open(jamTrackLink, '_blank');
}else{
history.push('/affiliate/agreement');
}
}
return (
<Card style={{ width: '75%' }} className="mx-auto affiliate-links">
<FalconCardHeader title={t('links.page_title')} titleClass="font-weight-semi-bold" />
@ -53,14 +83,14 @@ const JKAffiliateLinks = () => {
<div className="affiliate-links__subtitle">{t('links.home_page_subtitle')}</div>
<p>
{t('links.home_page_paragraph')}:{' '}
<a href={`https://www.jamkazam.com?affiliate=${affiliatePartnerId}`}>https://www.jamkazam.com?affiliate={affiliatePartnerId}</a>
<a href="#" onClick={handleClickOnHomePageLink}>{homePageLink}</a>
</p>
<div className="affiliate-links__subtitle">{t('links.jamtracks_pages_subtitle')}</div>
<p>{t('links.jamtracks_pages_paragraph')}</p>
<div className='mt-4'>
<JKJamTracksAutoComplete onSelect={handleOnSelect} />
</div>
{jamTracks &&
{jamTracks &&
jamTracks.map(jamTrack => {
return (
<div key={jamTrack.id}>
@ -68,8 +98,8 @@ const JKAffiliateLinks = () => {
{t('links.affiliate_link_for')} {jamTrack.name} {t('links.by')} {jamTrack.original_artist}:
</div>
<div>
<a target='_blank' href={`https://jamkazam.com/jamtrack/landing/${jamTrack.plan_code}?affiliate=${affiliatePartnerId}`}>
https://jamkazam.com/jamtrack/landing/{jamTrack.plan_code}?affiliate={affiliatePartnerId}
<a href="#" onClick={handleOnClickJamTrackLink}>
{jamTrackLink(jamTrack)}
</a>
</div>
</div>

View File

@ -23,7 +23,6 @@ const JKAffiliateSignups = () => {
return resp.json();
})
.then(data => {
console.log('signups', data);
const trafficByMonth = signupsByMonth(data.traffics);
setSignups(trafficByMonth);
setNextPage(data.next);

View File

@ -46,6 +46,7 @@ import JKAffiliatePayee from '../affiliate/JKAffiliatePayee';
import JKAffiliateLinks from '../affiliate/JKAffiliateLinks';
import JKAffiliateSignups from '../affiliate/JKAffiliateSignups';
import JKAffiliateEarnings from '../affiliate/JKAffiliateEarnings';
import JKAffiliateAgreement from '../affiliate/JKAffiliateAgreement';
//import loadable from '@loadable/component';
@ -280,6 +281,7 @@ function JKDashboardMain() {
<PrivateRoute path="/affiliate/links" component={JKAffiliateLinks} />
<PrivateRoute path="/affiliate/signups" component={JKAffiliateSignups} />
<PrivateRoute path="/affiliate/earnings" component={JKAffiliateEarnings} />
<PrivateRoute path="/affiliate/agreement" component={JKAffiliateAgreement} />
{/*Redirect*/}
<Redirect to="/errors/404" />
</Switch>

View File

@ -329,6 +329,16 @@ export const getInvoiceHistory = (options = {}) => {
});
}
export const createAffiliatePartner = (options) => {
return new Promise((resolve, reject) => {
apiFetch('/affiliate_partners', {
method: 'POST',
body: JSON.stringify(options)
})
.then(response => resolve(response))
.catch(error => reject(error))
});
}
export const getAffiliatePartnerData = (userId) => {
return new Promise((resolve, reject) => {

View File

@ -252,4 +252,26 @@ export const formatDateShort = (dateString) => {
return months[date.getMonth()] + ' ' + date.getDate() + ', ' + date.getFullYear();
}
export const groupByKey = (list, key) => list.reduce((hash, obj) => ({...hash, [obj[key]]:( hash[obj[key]] || [] ).concat(obj)}), {})
// returns Fri May 20, 2013
export const formatDate = (dateString, options = {}) => {
if (!dateString) {
return 'N/A'
}
const suppressDay = options.suppressDay || false;
const date = new Date(dateString);
return (suppressDay ? '' : (days[date.getDay()] + ' ')) + months[date.getMonth()] + ' ' + padString(date.getDate(), 2) + ', ' + date.getFullYear();
}
export const groupByKey = (list, key) => list.reduce((hash, obj) => ({...hash, [obj[key]]:( hash[obj[key]] || [] ).concat(obj)}), {})
const padString = (str, max) => {
let retVal = '' + str;
while (retVal.length < max) {
retVal = '0' + retVal;
}
return retVal;
}

View File

@ -1,23 +0,0 @@
import { useCallback } from 'react';
import { getAffiliatePartnerData } from '../helpers/rest';
import { useAuth } from '../context/UserAuth';
export const useFetchAffiliate = () => {
const { currentUser } = useAuth();
const fetchAffiliate = useCallback(async () => {
return new Promise(async (resolve, reject) => {
if (!currentUser) {
reject('No user');
}
try {
const response = await getAffiliatePartnerData(currentUser.id);
const affiliate = await response.json();
resolve(affiliate);
} catch (error) {
reject(error);
}
});
}, [currentUser]);
return { fetchAffiliate };
};

View File

@ -90,5 +90,18 @@
"earnings": "Earnings"
}
}
},
"agreement": {
"page_title": "JamKazam Affiliate Agreement",
"executed_on": "you executed this Agreement on",
"agreement_note": "By clicking the \"I Agree\" button below, I certify that I have the authority to enter into this Agreement on behalf of myself as an individual or on behalf of the entity I have listed below, and I further certify that I have read, understood, and agree to be bound by the terms above.",
"form": {
"submit": "I Agree",
"disagree": "I Disagree",
"individual": "I am entering into this Agreement as an individual",
"entity": "I am executing this Agreement on behalf of the company or entity listed below",
"entity_name": "Company or Entity Name",
"entity_type": "Company or Entity Type"
}
}
}