Ghost/ghost/admin/app/components/gh-member-settings-form-cp.js
Rishabh b5bc870a3b Handled dummy subscription for comped members
refs https://github.com/TryGhost/Team/issues/986#issuecomment-900281186

The tiers features updated the "comped" functionality to no longer create a Stripe Subscription for members. Since theme developers expect comped members will have a subscription, we added a dummy subscription object for comped members in API.

Since Admin previously expected comped members to be ones without any subscription but access to product, this change updates Admin behavior to identify comped subscriptions on a product and show them the same way as before.
2021-08-26 22:06:10 +05:30

184 lines
5.2 KiB
JavaScript

import Component from '@glimmer/component';
import moment from 'moment';
import {action} from '@ember/object';
import {getNonDecimal, getSymbol} from 'ghost-admin/utils/currency';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {tracked} from '@glimmer/tracking';
export default class extends Component {
@service
membersUtils
@service
ghostPaths
@service
ajax
@service
store
@service
feature
constructor(...args) {
super(...args);
this.member = this.args.member;
this.scratchMember = this.args.scratchMember;
}
@tracked
showMemberProductModal = false;
get canShowStripeInfo() {
return !this.member.get('isNew') && this.membersUtils.isStripeEnabled;
}
get isAddComplimentaryAllowed() {
if (!this.membersUtils.isStripeEnabled) {
return false;
}
if (this.feature.get('multipleProducts')) {
return true;
}
let subscriptions = this.member.get('subscriptions') || [];
const hasZeroPriceSub = subscriptions.filter((sub) => {
return ['active', 'trialing', 'unpaid', 'past_due'].includes(sub.status);
}).find((sub) => {
return !sub?.price?.amount;
});
return !hasZeroPriceSub;
}
get products() {
let products = this.member.get('products') || [];
let subscriptions = this.member.get('subscriptions') || [];
let subscriptionData = subscriptions.filter((sub) => {
return !!sub.price;
}).map((sub) => {
return {
...sub,
startDate: sub.start_date ? moment(sub.start_date).format('D MMM YYYY') : '-',
validUntil: sub.current_period_end ? moment(sub.current_period_end).format('D MMM YYYY') : '-',
cancellationReason: sub.cancellation_reason,
price: {
...sub.price,
currencySymbol: getSymbol(sub.price.currency),
nonDecimalAmount: getNonDecimal(sub.price.amount)
},
isComplimentary: !sub.id
};
});
for (let product of products) {
let productSubscriptions = subscriptionData.filter((subscription) => {
if (subscription.status === 'canceled') {
return false;
}
return subscription?.price?.product?.product_id === product.id;
});
product.subscriptions = productSubscriptions;
}
return products;
}
get customer() {
let firstSubscription = this.member.get('subscriptions').firstObject;
let customer = firstSubscription?.customer;
if (customer) {
return {
...customer,
startDate: firstSubscription?.startDate
};
}
return null;
}
get isCreatingComplimentary() {
return this.args.isSaveRunning;
}
@action
setProperty(property, value) {
this.args.setProperty(property, value);
}
@action
setLabels(labels) {
this.member.set('labels', labels);
}
@action
closeMemberProductModal() {
this.showMemberProductModal = false;
}
@action
cancelSubscription(subscriptionId) {
this.cancelSubscriptionTask.perform(subscriptionId);
}
@action
removeComplimentary(productId) {
this.removeComplimentaryTask.perform(productId);
}
@action
continueSubscription(subscriptionId) {
this.continueSubscriptionTask.perform(subscriptionId);
}
@action
addCompedSubscription() {
this.args.setProperty('comped', true);
this.args.saveMember();
}
@task({drop: true})
*cancelSubscriptionTask(subscriptionId) {
let url = this.ghostPaths.url.api('members', this.member.get('id'), 'subscriptions', subscriptionId);
let response = yield this.ajax.put(url, {
data: {
cancel_at_period_end: true
}
});
this.store.pushPayload('member', response);
return response;
}
@task({drop: true})
*removeComplimentaryTask(productId) {
let url = this.ghostPaths.url.api(`members/${this.member.get('id')}`);
let products = this.member.get('products') || [];
const updatedProducts = products.filter(product => product.id !== productId).map(product => ({id: product.id}));
let response = yield this.ajax.put(url, {
data: {
members: [{
id: this.member.get('id'),
email: this.member.get('email'),
products: updatedProducts
}]
}
});
this.store.pushPayload('member', response);
return response;
}
@task({drop: true})
*continueSubscriptionTask(subscriptionId) {
let url = this.ghostPaths.url.api('members', this.member.get('id'), 'subscriptions', subscriptionId);
let response = yield this.ajax.put(url, {
data: {
cancel_at_period_end: false
}
});
this.store.pushPayload('member', response);
return response;
}
}