Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
HTML/CSS/JavaScript updates
Browse files Browse the repository at this point in the history
  • Loading branch information
chadwhitacre committed Sep 10, 2017
1 parent 2a3832c commit c4db9b2
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 80 deletions.
63 changes: 31 additions & 32 deletions js/gratipay/homepage.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,33 @@ Gratipay.homepage = {}
Gratipay.homepage.initForm = function(clientAuthorization) {
$form = $('#homepage #content form');

$submit= $form.find('button[type=submit]');
$submit.click(Gratipay.homepage.submitForm);

$chooseEcosystem = $form.find('.ecosystem-chooser button');
$chooseEcosystem.click(function(e) {
e.preventDefault();
Gratipay.notification('Not implemented.', 'error');
});

$promote = $form.find('.promotion-gate button');
$promote.click(Gratipay.homepage.openPromote);

function callback(createErr, instance) {
$submit = $form.find('button[type=submit]');
$submit.click(function(e) {
e.preventDefault();
instance.requestPaymentMethod(function(requestPaymentMethodErr, payload) {
Gratipay.homepage.submitFormWithNonce(payload.nonce);
});
});
}

braintree.dropin.create({
authorization: clientAuthorization,
container: '#braintree-container'
}, function (createErr, instance) {
$submit.click(function () {
instance.requestPaymentMethod(function (requestPaymentMethodErr, payload) {
// Submit payload.nonce to your server
});
});
});
}
}, callback);
};

Gratipay.homepage.submitForm = function(e) {
e.preventDefault();

$input = $(this)
$form = $(this).parent('form');
Gratipay.homepage.submitFormWithNonce = function(nonce) {
$submit = $form.find('button[type=submit]');
$form = $('#homepage #content form');
var data = new FormData($form[0]);
data.set('payment_method_nonce', nonce);

$input.prop('disable', true);
$submit.prop('disable', true);

$.ajax({
url: $form.attr('action'),
Expand All @@ -43,21 +38,25 @@ Gratipay.homepage.submitForm = function(e) {
processData: false,
contentType: false,
dataType: 'json',
success: function (d) {
$('a.team_url').attr('href', d.team_url).text(d.team_url);
$('a.review_url').attr('href', d.review_url).text(d.review_url);
$('form').slideUp(500, function() {
$('.application-complete').slideDown(250);
});
},
error: [Gratipay.error, function() { $input.prop('disable', false); }]
success: function(data) {
console.log(data);
// Due to Aspen limitations we use 200 for both success and failure. :/
if (data.errors.length > 0) {
$submit.prop('disable', false);
Gratipay.notification(data.msg, 'error');
} else {
$('.payment-complete a.receipt').attr('href', data.receipt_url);
$('.payment-complete').slideDown(200);
$('form').slideUp(500);
}
}
});
}
};

Gratipay.homepage.openPromote = function(e) {
e.preventDefault();
$('.promotion-gate').fadeOut();
$('.promotion-fields').slideDown(function() {
$('.promotion-fields input:first').focus();
});
}
};
2 changes: 1 addition & 1 deletion scss/elements/buttons-knobs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ button.selected:hover:not(:disabled), button.selected.drag,
margin: 30px auto 30px;
text-align: center;

button.large {
button.large, a.button.large {
font: normal 16px/32px $Ideal;
padding: 10px 16px;
border-radius: 5px;
Expand Down
8 changes: 8 additions & 0 deletions scss/pages/homepage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -183,5 +183,13 @@
display: none;
}
}
.payment-complete {
.fine-print {
padding: 0;
}
.twitter-container {
padding-top: 20px;
}
}
}
}
14 changes: 14 additions & 0 deletions tests/py/test_www_homepage.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,17 @@ def test_post_gets_json(self):
assert response.code == 200
assert response.headers['Content-Type'] == 'application/json'
assert json.loads(response.body) == {'parsed': {}, 'errors': []}

def test_bad_post_gets_400(self):
response = self.client.PxST('/', data=BAD, HTTP_ACCEPT=b'application/json')
assert response.code == 400
assert response.headers['Content-Type'] == 'application/json'
scrubbed = SCRUBBED.copy()
scrubbed.pop('payment_method_nonce') # consumed
assert json.loads(response.body) == {'parsed': scrubbed, 'errors': ALL}

def test_really_bad_post_gets_plain_400(self):
response = self.client.PxST('/', data={}, HTTP_ACCEPT=b'application/json')
assert response.code == 400
assert response.headers == {}
assert response.body == "Missing key: u'amount'"
35 changes: 35 additions & 0 deletions tests/ttw/test_homepage.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@

class Tests(BrowserHarness):

def fetch(self):
return self.db.one('SELECT pfos.*::payments_for_open_source '
'FROM payments_for_open_source pfos')

def fill_form(self, amount, credit_card_number, expiration, cvv, name, email_address,
follow_up, promotion_name, promotion_url, promotion_twitter, promotion_message):
self.wait_for('.braintree-form-number')
self.fill('amount', amount)
with self.get_iframe('braintree-hosted-field-number') as iframe:
iframe.fill('credit-card-number', credit_card_number)
with self.get_iframe('braintree-hosted-field-expirationDate') as iframe:
iframe.fill('expiration', expiration)
with self.get_iframe('braintree-hosted-field-cvv') as iframe:
iframe.fill('cvv', cvv)
self.fill('name', name)
self.fill('email_address', email_address)
if promotion_name:
self.css('.promotion-gate button').type('\n')
# stackoverflow.com/q/11908249#comment58577676_19763087
self.wait_for('#promotion-message')
self.fill('promotion_name', promotion_name)
self.fill('promotion_url', promotion_url)
self.fill('promotion_twitter', promotion_twitter)
self.fill('promotion_message', promotion_message)


def test_loads_for_anon(self):
assert self.css('#banner h1').html == 'Pay for open source.'
assert self.css('#header .sign-in button').html.strip()[:17] == 'Sign in / Sign up'
Expand All @@ -15,3 +41,12 @@ def test_redirects_for_authed_exclamation_point(self):
self.reload()
assert self.css('#banner h1').html == 'Browse'
assert self.css('.you-are a').html.strip()[:6] == '~alice'

def test_anon_can_post(self):
self.fill_form('537', '4242424242424242', '1020', '123', 'Alice Liddell',
'[email protected]', 'monthly', 'Wonderland', 'http://www.example.com/',
'thebestbutter', 'Love me! Love me! Say that you love me!')
self.css('fieldset.submit button').type('\n')
self.wait_for('.payment-complete', 10)
assert self.css('.payment-complete .description').text == 'Payment complete.'
assert self.fetch().succeeded
114 changes: 67 additions & 47 deletions www/index.spt
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
import json
from aspen import Response
from gratipay.homepage import pay_for_open_source
[---]
if not user.ANON:
Expand All @@ -7,6 +9,10 @@ suppress_sidebar = True
page_id = "homepage"
banner = "Pay for Open Source"
result = pay_for_open_source(website.app, request.body) if request.method == 'POST' else {}
if result and result['errors']:
# Hmmm ... bit of an Aspen rough spot ... interaction w/ error.spt, skip it
# by overriding 200 for both success and failure. :(
result['msg'] = _("Sorry, we could not process your payment.")
[---] application/json via json_dump
result
[---] text/html
Expand Down Expand Up @@ -37,7 +43,43 @@ $(document).ready(function() {
{% endblock %}

{% block content %}
<form action="/companies/create.json" method="POST">

<div class="payment-complete" style="display: none;">
<div class="description important-thing-at-the-top">
{{ _("Payment complete.") }}
</div>
<p class="instructions">
{{ _("Thank you for your purchase!") }}
</p>
<div class="important-button" class="important-button">
<a class="button large selected receipt" href="">{{ _("View Receipt") }}</a>
</div>
<p class="fine-print">
{{ _( "Email {support} with{br}any questions or feedback."
, support='<a href="mailto:[email protected]">[email protected]</a>'|safe
, br='<br>'|safe
) }}
</p>
<div class="twitter-container">
<a class="twitter-share-button"
href="https://twitter.com/intent/tweet?text=We%20just%20paid%20for%20#opensource%20on%20@Gratipay!"
data-size="large" data-url="https://gratipay.com/"></a>
<script>window.twttr = (function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0], t = window.twttr || {};
if (d.getElementById(id)) return t;
js = d.createElement(s);
js.id = id;
js.src = "https://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);

t._e = [];
t.ready = function(f) { t._e.push(f); };
return t;
}(document, "script", "twitter-wjs"));</script>
</div>
</div>

<form action="/" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">

<fieldset>
Expand All @@ -62,20 +104,21 @@ $(document).ready(function() {
<div id="braintree-container"></div>
</fieldset>

<fieldset>
<legend>{{ _('Step 2 of 3') }}</legend>
<fieldset class="optional">
<legend>{{ _('Optional') }}</legend>

<h2>{{ _('Who are you?') }}</h2>

<div class="field">
<label for="name">{{ _('Your Name') }}</label>
<input name="name" id="name" type="text" required>
<input name="name" id="name" type="text">
</div>

<div class="field email-address">
<label for="email-address">{{ _('Your Email Address') }}</label>
<input name="email-address" id="email-address" type="text" required>
<input name="email_address" id="email-address" type="text">
<p class="fine-print">
{{ _('You will get a verification link before we finalize your payment.') }}
{{ _('You will get a receipt for your payment.') }}
</p>
</div>

Expand All @@ -84,23 +127,23 @@ $(document).ready(function() {
<legend>{{ _('Follow-up') }}</legend>

<div class="fancy-radio">
<input type="radio" name="follow-up" id="follow-up-monthly" value="monthly">
<input type="radio" name="follow_up" id="follow-up-monthly" value="monthly">
<label for="follow-up-monthly">{{ _('Monthly') }}</label>
</div>

<div class="fancy-radio">
<input type="radio" name="follow-up" id="follow-up-quarterly"
<input type="radio" name="follow_up" id="follow-up-quarterly"
value="quarterly" checked>
<label for="follow-up-quarterly">{{ _('Quarterly') }}</label>
</div>

<div class="fancy-radio">
<input type="radio" name="follow-up" id="follow-up-yearly" value="yearly">
<input type="radio" name="follow_up" id="follow-up-yearly" value="yearly">
<label for="follow-up-yearly">{{ _('Yearly') }}</label>
</div>

<div class="fancy-radio">
<input type="radio" name="follow-up" id="follow-up-never" value="never">
<input type="radio" name="follow_up" id="follow-up-never" value="never">
<label for="follow-up-never">{{ _('Never') }}</label>
</div>

Expand All @@ -109,19 +152,6 @@ $(document).ready(function() {
</p>
</fieldset>
</div>
</fieldset>

<fieldset class="optional">
<legend>{{ _('Optional') }}</legend>
<h2>{{ _('Ecosystems') }}</h2>
<p class="fine-print">
{{ _('Which do you value most?{br}(JavaScript, Python, etc.)', br='<br>'|safe) }}
</p>

<div class="important-button ecosystem-chooser">
<button class="large">{{ _('Choose an Ecosystem') }}</button>
</div>
<div class="ecosystems"></div>


<h2>{{ _('Promotion') }}</h2>
Expand All @@ -130,23 +160,24 @@ $(document).ready(function() {
<p class="fine-print">{{ _('Thanks! We are excited to brag about you!') }}</p>

<div class="field">
<label label="company">{{ _("Your Company Name") }}</label>
<input type="text" name="promote-name" id="promote-name">
<label for="promotion-name">{{ _("Your Company Name") }}</label>
<input type="text" name="promotion_name" id="promotion-name">
</div>

<div class="field">
<label label="company">{{ _("Your Landing Page URL") }}</label>
<input type="text" name="promote-url" id="promote-url">
<label for="promotion-url">{{ _("Your Landing Page URL") }}</label>
<input type="text" name="promotion_url" id="promotion-url">
</div>

<div class="field">
<label label="company">{{ _("Your Company Twitter Handle") }}</label>
<input type="text" name="promote-twitter" id="promote-twitter">
<label for="promotion-twitter">{{ _("Your Company Twitter Handle") }}</label>
<input type="text" name="promotion_twitter" id="promotion-twitter">
</div>

<div class="field">
<label label="company">{{ _("Your Message to the Open Source Community") }}</label>
<input type="text" name="promote-message" id="promote-message">
<label for="promotion-message">
{{ _("Your Message to the Open Source Community") }}</label>
<input type="text" name="promotion_message" id="promotion-message">
</div>
</div>

Expand All @@ -157,33 +188,22 @@ $(document).ready(function() {

</fieldset>

<fieldset>
<fieldset class="submit">
<legend>{{ _('Submit Form') }}</legend>

<div class="important-button">
<button type="submit" class="apply selected large">
<button type="submit" class="selected large">
{{ _("Pay for Open Source") }}
</button>
</div>
<p class="fine-print">
{{ _( "By submitting this form you agree to our {0}terms of service{1}."
, '<a href="/about/policies/terms-of-service">'|safe
, '</a>'|safe
{{ _( "By submitting this form you agree to our {a}terms of service{_a}."
, a='<a href="/about/policies/terms-of-service">'|safe
, _a='</a>'|safe
) }}
</p>
</fieldset>

</form>

<div class="application-complete" style="display: none;">
<p>{{ _("Thanks! Your public project page is:") }}</p>
<p><a href="" class="team_url"></a></p>
<p>{{ _("And your public review ticket is:") }}</p>
<p><a href="" class="review_url"></a></p>
<p>{{ _( "You can watch and participate in our review process there. We will send a notification to {email} when we finish our review."
, email=user.participant.email_address
) }}</p>
<p>{{ _("Thanks for applying!") }}</p>
</div>

{% endblock %}

0 comments on commit c4db9b2

Please sign in to comment.