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