mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) suspend a team site after an AppSumo refund
Summary: This suspends service to a team site for which an AppSumo refund has been made, and nudges users to their free personal account. I expect that a refund request would fail for a site where user is also paying us for extra seats. Test Plan: tested manually Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2912
This commit is contained in:
parent
6e15d44cf6
commit
1ce5e98996
@ -104,7 +104,7 @@ export class TopAppModelImpl extends Disposable implements TopAppModel {
|
|||||||
org.billingAccount.product.name === 'suspended') {
|
org.billingAccount.product.name === 'suspended') {
|
||||||
this.notifier.createUserError(
|
this.notifier.createUserError(
|
||||||
'This team site is suspended. Documents can be read, but not modified.',
|
'This team site is suspended. Documents can be read, but not modified.',
|
||||||
{actions: ['renew']}
|
{actions: ['renew', 'personal']}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ export interface IProgress extends Expirable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Identifies supported actions. These are implemented in NotifyUI.
|
// Identifies supported actions. These are implemented in NotifyUI.
|
||||||
export type NotifyAction = 'upgrade' | 'renew' | 'report-problem' | 'ask-for-help';
|
export type NotifyAction = 'upgrade' | 'renew' | 'personal' | 'report-problem' | 'ask-for-help';
|
||||||
|
|
||||||
export interface INotifyOptions {
|
export interface INotifyOptions {
|
||||||
message: string | (() => DomElementArg); // A string, or a function that builds dom.
|
message: string | (() => DomElementArg); // A string, or a function that builds dom.
|
||||||
|
@ -146,6 +146,7 @@ export class BillingPage extends Disposable {
|
|||||||
// name and minimizing the plan.
|
// name and minimizing the plan.
|
||||||
const tier = sub.discountName && sub.discountName.includes(' Tier ');
|
const tier = sub.discountName && sub.discountName.includes(' Tier ');
|
||||||
const planName = tier ? sub.discountName! : sub.activePlan.nickname;
|
const planName = tier ? sub.discountName! : sub.activePlan.nickname;
|
||||||
|
const invoiceId = this._appModel.currentOrg?.billingAccount?.externalOptions?.invoiceId;
|
||||||
return [
|
return [
|
||||||
css.summaryFeatures(
|
css.summaryFeatures(
|
||||||
validPlan ? [
|
validPlan ? [
|
||||||
@ -182,7 +183,7 @@ export class BillingPage extends Disposable {
|
|||||||
makeSummaryFeature([`Your next invoice is `, getPriceString(sub.nextTotal),
|
makeSummaryFeature([`Your next invoice is `, getPriceString(sub.nextTotal),
|
||||||
' on ', dateFmt(sub.periodEnd)]),
|
' on ', dateFmt(sub.periodEnd)]),
|
||||||
] : null,
|
] : null,
|
||||||
tier ? this.buildAppSumoLink(this._appModel.currentOrg?.billingAccount?.externalOptions?.invoiceId) : null,
|
invoiceId ? this.buildAppSumoLink(invoiceId) : null,
|
||||||
getSubscriptionProblem(sub),
|
getSubscriptionProblem(sub),
|
||||||
testId('summary')
|
testId('summary')
|
||||||
),
|
),
|
||||||
@ -252,8 +253,7 @@ export class BillingPage extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Include a precise link back to AppSumo for changing plans.
|
// Include a precise link back to AppSumo for changing plans.
|
||||||
public buildAppSumoLink(invoiceId: string | undefined) {
|
public buildAppSumoLink(invoiceId: string) {
|
||||||
if (!invoiceId) { return null; }
|
|
||||||
return dom('div',
|
return dom('div',
|
||||||
css.billingTextBtn({ style: 'margin: 10px 0;' },
|
css.billingTextBtn({ style: 'margin: 10px 0;' },
|
||||||
cssBreadcrumbsLink(
|
cssBreadcrumbsLink(
|
||||||
|
@ -31,6 +31,17 @@ function buildAction(action: NotifyAction, item: Notification, options: IBeaconO
|
|||||||
return dom('a', cssToastAction.cls(''), 'Renew', {target: '_blank'},
|
return dom('a', cssToastAction.cls(''), 'Renew', {target: '_blank'},
|
||||||
{href: urlState().makeUrl({billing: 'billing'})});
|
{href: urlState().makeUrl({billing: 'billing'})});
|
||||||
|
|
||||||
|
case 'personal':
|
||||||
|
if (!appModel) { return null; }
|
||||||
|
return cssToastAction('Go to your free personal site', dom.on('click', async () => {
|
||||||
|
const info = await appModel.api.getSessionAll();
|
||||||
|
const orgs = info.orgs.filter(org => org.owner && org.owner.id === appModel.currentUser?.id);
|
||||||
|
if (orgs.length !== 1) {
|
||||||
|
throw new Error('Cannot find personal site, sorry!');
|
||||||
|
}
|
||||||
|
window.location.assign(urlState().makeUrl({org: orgs[0].domain || undefined}));
|
||||||
|
}));
|
||||||
|
|
||||||
case 'report-problem':
|
case 'report-problem':
|
||||||
return cssToastAction('Report a problem', testId('toast-report-problem'),
|
return cssToastAction('Report a problem', testId('toast-report-problem'),
|
||||||
dom.on('click', () => beaconOpenMessage({...options, includeAppErrors: true})));
|
dom.on('click', () => beaconOpenMessage({...options, includeAppErrors: true})));
|
||||||
|
@ -110,8 +110,9 @@ export function getDefaultProductNames() {
|
|||||||
return {
|
return {
|
||||||
personal: 'starter', // Personal site start off on a functional plan.
|
personal: 'starter', // Personal site start off on a functional plan.
|
||||||
teamInitial: 'stub', // Team site starts off on a limited plan, requiring subscription.
|
teamInitial: 'stub', // Team site starts off on a limited plan, requiring subscription.
|
||||||
team: 'team', // Functional team site
|
teamCancel: 'suspended', // Team site that has been 'turned off'.
|
||||||
};
|
team: 'team', // Functional team site.
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user