initial components
This commit is contained in:
parent
af97581911
commit
ee2be672f7
|
@ -0,0 +1,42 @@
|
||||||
|
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
@computed("confirmation.card.last4")
|
||||||
|
last4() {
|
||||||
|
return this.get("confirmation.card.last4");
|
||||||
|
},
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
const settings = Discourse.SiteSettings;
|
||||||
|
|
||||||
|
this.setProperties({
|
||||||
|
confirmation: false,
|
||||||
|
currency: settings.discourse_donations_currency,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
closeModal() {
|
||||||
|
this.set('paymentError', false);
|
||||||
|
this.set('confirmation', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleConfirmStripeCard(paymentMethod) {
|
||||||
|
this.set('confirmation', paymentMethod);
|
||||||
|
},
|
||||||
|
|
||||||
|
confirmStripeCard() {
|
||||||
|
const paymentMethodId = this.confirmation.id;
|
||||||
|
this.stripePaymentHandler(paymentMethodId, this.amount).then((paymentIntent) => {
|
||||||
|
if (paymentIntent.error) {
|
||||||
|
this.set('paymentError', paymentIntent.error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('ok done');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNames: "donation-list",
|
||||||
|
hasSubscriptions: Ember.computed.notEmpty("subscriptions"),
|
||||||
|
hasCharges: Ember.computed.notEmpty("charges")
|
||||||
|
});
|
|
@ -0,0 +1,99 @@
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
import { formatAnchor, formatAmount } from "../lib/donation-utilities";
|
||||||
|
import { default as computed } from "ember-addons/ember-computed-decorators";
|
||||||
|
import showModal from "discourse/lib/show-modal";
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNameBindings: [":donation-row", "canceled", "updating"],
|
||||||
|
includePrefix: Ember.computed.or("invoice", "charge"),
|
||||||
|
canceled: Ember.computed.equal("subscription.status", "canceled"),
|
||||||
|
|
||||||
|
@computed("subscription", "invoice", "charge", "customer")
|
||||||
|
data(subscription, invoice, charge, customer) {
|
||||||
|
if (subscription) {
|
||||||
|
return $.extend({}, subscription.plan, {
|
||||||
|
anchor: subscription.billing_cycle_anchor
|
||||||
|
});
|
||||||
|
} else if (invoice) {
|
||||||
|
let receiptSent = false;
|
||||||
|
|
||||||
|
if (invoice.receipt_number && customer.email) {
|
||||||
|
receiptSent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $.extend({}, invoice.lines.data[0], {
|
||||||
|
anchor: invoice.date,
|
||||||
|
invoiceLink: invoice.invoice_pdf,
|
||||||
|
receiptSent
|
||||||
|
});
|
||||||
|
} else if (charge) {
|
||||||
|
let receiptSent = false;
|
||||||
|
|
||||||
|
if (charge.receipt_number && charge.receipt_email) {
|
||||||
|
receiptSent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $.extend({}, charge, {
|
||||||
|
anchor: charge.created,
|
||||||
|
receiptSent
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed("data.currency")
|
||||||
|
currency(currency) {
|
||||||
|
return currency ? currency.toUpperCase() : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed("data.amount", "currency")
|
||||||
|
amount(amount, currency) {
|
||||||
|
return formatAmount(amount, currency);
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed("data.interval")
|
||||||
|
interval(interval) {
|
||||||
|
return interval || "once";
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed("data.anchor", "interval")
|
||||||
|
period(anchor, interval) {
|
||||||
|
return I18n.t(`discourse_donations.period.${interval}`, {
|
||||||
|
anchor: formatAnchor(interval, moment.unix(anchor))
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelSubscription() {
|
||||||
|
const subscriptionId = this.get("subscription.id");
|
||||||
|
this.set("updating", true);
|
||||||
|
|
||||||
|
ajax("/donate/charges/cancel-subscription", {
|
||||||
|
data: {
|
||||||
|
subscription_id: subscriptionId
|
||||||
|
},
|
||||||
|
method: "put"
|
||||||
|
})
|
||||||
|
.then(result => {
|
||||||
|
if (result.success) {
|
||||||
|
this.set("subscription", result.subscription);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError)
|
||||||
|
.finally(() => {
|
||||||
|
this.set("updating", false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
cancelSubscription() {
|
||||||
|
showModal("cancel-subscription", {
|
||||||
|
model: {
|
||||||
|
currency: this.get("currency"),
|
||||||
|
amount: this.get("amount"),
|
||||||
|
period: this.get("period"),
|
||||||
|
confirm: () => this.cancelSubscription()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
const settings = Discourse.SiteSettings;
|
||||||
|
|
||||||
|
this.setProperties({
|
||||||
|
cardError: false,
|
||||||
|
color: jQuery("body").css("color"),
|
||||||
|
backgroundColor: jQuery("body").css("background-color"),
|
||||||
|
stripe: Stripe(settings.discourse_patrons_public_key),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
const color = this.get('color');
|
||||||
|
|
||||||
|
const style = {
|
||||||
|
base: {
|
||||||
|
color,
|
||||||
|
iconColor: color,
|
||||||
|
"::placeholder": { color }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const elements = this.stripe.elements();
|
||||||
|
const card = elements.create("card", { style, hidePostalCode: true });
|
||||||
|
|
||||||
|
card.mount('#card-element');
|
||||||
|
|
||||||
|
this.set("card", card);
|
||||||
|
|
||||||
|
card.on("change", (result) => {
|
||||||
|
this.set('cardError', false);
|
||||||
|
|
||||||
|
if(result.error) {
|
||||||
|
this.set('cardError', result.error.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
submitStripeCard() {
|
||||||
|
this.stripe.createPaymentMethod('card', this.card).then((result) => {
|
||||||
|
if (result.error) {
|
||||||
|
this.set('cardError', result.error.message);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.handleConfirmStripeCard(result.paymentMethod);
|
||||||
|
}
|
||||||
|
}, () => {
|
||||||
|
this.set('cardError', 'Unknown error.');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,57 @@
|
||||||
|
|
||||||
|
{{#if confirmation}}
|
||||||
|
{{#d-modal closeModal=(action "closeModal") modalStyle="inline-modal" title=(i18n "discourse_donations.confirm")}}
|
||||||
|
{{#d-modal-body}}
|
||||||
|
<table class="discourse-donations-confirmation">
|
||||||
|
<tr>
|
||||||
|
<td>Amount</td>
|
||||||
|
<td class="discourse-donations-amount">{{amount}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Card</td>
|
||||||
|
<td class="discourse-donations-last4">.... .... .... {{last4}}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{/d-modal-body}}
|
||||||
|
|
||||||
|
<div class='modal-footer'>
|
||||||
|
{{#d-button action="confirmStripeCard" class="btn btn-primary btn-payment"}}
|
||||||
|
{{i18n 'discourse_donations.submit'}} {{amount}}
|
||||||
|
{{/d-button}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{/d-modal}}
|
||||||
|
|
||||||
|
{{#if paymentError}}
|
||||||
|
<div class="popup-tip bad">
|
||||||
|
{{paymentError}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{else}}
|
||||||
|
<div class="discourse-donations-section-columns">
|
||||||
|
<div class="section-column">
|
||||||
|
<h3>Your information</h3>
|
||||||
|
|
||||||
|
<div class="user-controls">
|
||||||
|
<div class="display-row">
|
||||||
|
<div class="field">Payment Amount</div>
|
||||||
|
<div class="value">
|
||||||
|
{{input value=amount}}<br>
|
||||||
|
Enter {{currency}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="section-column">
|
||||||
|
<div style>
|
||||||
|
{{stripe-card
|
||||||
|
amount=amount
|
||||||
|
currency=currency
|
||||||
|
handleConfirmStripeCard=(action "handleConfirmStripeCard")
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{{#if hasSubscriptions}}
|
||||||
|
<div class="subscription-list">
|
||||||
|
<div class="underline">{{i18n 'discourse_donations.donations.subscriptions'}}</div>
|
||||||
|
<ul>
|
||||||
|
{{#each subscriptions as |s|}}
|
||||||
|
<li>{{donation-row subscription=s.subscription customer=customer new=s.new}}</li>
|
||||||
|
{{#if s.invoices}}
|
||||||
|
<ul>
|
||||||
|
{{#each s.invoices as |invoice|}}
|
||||||
|
<li>{{donation-row invoice=invoice customer=customer new=s.new}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if hasCharges}}
|
||||||
|
<div class="charge-list">
|
||||||
|
<div class='underline'>{{i18n 'discourse_donations.donations.charges'}}</div>
|
||||||
|
<ul>
|
||||||
|
{{#each charges as |charge|}}
|
||||||
|
<li>{{donation-row charge=charge customer=customer new=charge.new}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
|
@ -0,0 +1,41 @@
|
||||||
|
{{#if includePrefix}}
|
||||||
|
<span>{{i18n 'discourse_donations.invoice_prefix'}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<span class="donation-row-currency">{{currency}}</span>
|
||||||
|
|
||||||
|
<span class="donation-row-amount">{{amount}}</span>
|
||||||
|
|
||||||
|
<span class="donation-row-period">{{period}}</span>
|
||||||
|
|
||||||
|
{{#if invoice}}
|
||||||
|
<a href='{{data.invoiceLink}}' target='_blank'>({{i18n 'discourse_donations.invoice'}})</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if currentUser}}
|
||||||
|
{{#if subscription}}
|
||||||
|
<span class="donation-row-subscription">
|
||||||
|
{{#if updating}}
|
||||||
|
{{loading-spinner size='small'}}
|
||||||
|
{{else}}
|
||||||
|
{{#unless canceled}}
|
||||||
|
<a {{action 'cancelSubscription'}}>
|
||||||
|
{{i18n 'cancel'}}
|
||||||
|
</a>
|
||||||
|
{{/unless}}
|
||||||
|
{{/if}}
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if receiptSent}}
|
||||||
|
<span>–</span>
|
||||||
|
<span>{{i18n 'discourse_donations.receipt' email=customer.email}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if new}}
|
||||||
|
<span class="new-flag">
|
||||||
|
{{d-icon 'circle'}}
|
||||||
|
<span>{{i18n 'new_item'}}</span>
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
<h3>Credit card information</h3>
|
||||||
|
|
||||||
|
<div id="card-element"></div>
|
||||||
|
|
||||||
|
<div id="card-action">
|
||||||
|
{{#d-button action="submitStripeCard" class="btn btn-primary btn-payment"}}
|
||||||
|
{{i18n 'discourse_donations.confirm'}}<br>{{amount}}
|
||||||
|
{{/d-button}}
|
||||||
|
|
||||||
|
{{#if cardError}}
|
||||||
|
<div class="popup-tip bad">
|
||||||
|
{{cardError}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
|
@ -0,0 +1,45 @@
|
||||||
|
import componentTest from "helpers/component-test";
|
||||||
|
|
||||||
|
moduleForComponent("donation-form", { integration: true });
|
||||||
|
|
||||||
|
componentTest("Discourse Patrons donation form has content", {
|
||||||
|
template: `{{donation-form}}`,
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.registry.register(
|
||||||
|
"component:stripe-card",
|
||||||
|
Ember.Component.extend({ tagName: "dummy-component-tag" })
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
async test(assert) {
|
||||||
|
assert.ok(find("#payment-form").length, "The form renders");
|
||||||
|
assert.ok(
|
||||||
|
find("dummy-component-tag").length,
|
||||||
|
"The stripe component renders"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest("donation form has a confirmation", {
|
||||||
|
template: `{{donation-form confirmation=confirmation}}`,
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.registry.register(
|
||||||
|
"component:stripe-card",
|
||||||
|
Ember.Component.extend()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
async test(assert) {
|
||||||
|
this.set("confirmation", { "card": { "last4": "4242" }});
|
||||||
|
|
||||||
|
const confirmExists = find(".discourse-donations-confirmation").length;
|
||||||
|
|
||||||
|
assert.ok(confirmExists, "The confirmation form renders");
|
||||||
|
|
||||||
|
const last4 = find(".discourse-donations-last4").text().trim();
|
||||||
|
|
||||||
|
assert.equal(last4, ".... .... .... 4242", "The last 4 digits are correct");
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
import componentTest from "helpers/component-test";
|
||||||
|
|
||||||
|
moduleForComponent("donation-row", { integration: true });
|
||||||
|
|
||||||
|
componentTest("Discourse Patrons donation-row", {
|
||||||
|
template: `{{donation-row currency=3 amount=21 period='monthly'}}`,
|
||||||
|
|
||||||
|
test(assert) {
|
||||||
|
assert.equal(find(".donation-row-currency").text(), "3", "It has currency");
|
||||||
|
assert.equal(find(".donation-row-amount").text(), "21", "It has an amount");
|
||||||
|
assert.equal(
|
||||||
|
find(".donation-row-period").text(),
|
||||||
|
"monthly",
|
||||||
|
"It has a period"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
componentTest("donation-row cancels subscription", {
|
||||||
|
template: `{{donation-row currentUser=currentUser subscription=subscription}}`,
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
this.set("currentUser", true);
|
||||||
|
this.set("subscription", true);
|
||||||
|
},
|
||||||
|
|
||||||
|
async test(assert) {
|
||||||
|
assert.ok(
|
||||||
|
find(".donation-row-subscription").length,
|
||||||
|
"It has a subscription"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,40 @@
|
||||||
|
import componentTest from "helpers/component-test";
|
||||||
|
|
||||||
|
moduleForComponent("stripe-card", { integration: true });
|
||||||
|
|
||||||
|
componentTest("Discourse Patrons stripe card success", {
|
||||||
|
template: `{{stripe-card handleConfirmStripeCard=onSubmit}}`,
|
||||||
|
|
||||||
|
beforeEach() {
|
||||||
|
window.Stripe = () => {
|
||||||
|
return {
|
||||||
|
createPaymentMethod() {
|
||||||
|
return new Ember.RSVP.Promise((resolve) => {
|
||||||
|
resolve('payment-method-response');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
elements() {
|
||||||
|
return {
|
||||||
|
create() {
|
||||||
|
return {
|
||||||
|
on() {},
|
||||||
|
card() {},
|
||||||
|
mount() {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async test(assert) {
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
|
this.set("onSubmit", (arg) => {
|
||||||
|
assert.equal(arg, "payment-method-response", "payment method created");
|
||||||
|
});
|
||||||
|
|
||||||
|
await click(".btn-payment");
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in New Issue