(core) Customizable stripe plans.

Summary:
- Reading plans from Stripe, and allowing Stripe to define custom plans.
- Storing product features (aka limits) in Stripe, that override those in db.
- Adding hierarchical data in Stripe. All features are defined at Product level but can be overwritten on Price levels.
- New options for Support user to
-- Override product for team site (if he is added as a billing manager)
-- Override subscription and customer id for a team site
-- Attach an "offer", an custom plan configured in stripe that a team site can use
-- Enabling wire transfer for subscription by allowing subscription to be created without a payment method (which is customizable)

Test Plan: Updated and new.

Reviewers: georgegevoian

Reviewed By: georgegevoian

Differential Revision: https://phab.getgrist.com/D4201
This commit is contained in:
Jarosław Sadziński
2024-05-17 21:14:34 +02:00
parent ed9514bae0
commit 60423edc17
40 changed files with 720 additions and 248 deletions

View File

@@ -1,5 +1,5 @@
import {theme} from 'app/client/ui2018/cssVars';
import {DomArg, keyframes, styled} from 'grainjs';
import {DomArg, keyframes, Observable, observable, styled} from 'grainjs';
const rotate360 = keyframes(`
from { transform: rotate(45deg); }
@@ -42,6 +42,21 @@ export function loadingDots(...args: DomArg<HTMLDivElement>[]) {
);
}
export function watchPromise<T extends (...args: any[]) => any>(fun: T): T & {busy: Observable<boolean>} {
const loading = observable(false);
const result = async (...args: any) => {
loading.set(true);
try {
return await fun(...args);
} finally {
if (!loading.isDisposed()) {
loading.set(false);
}
}
};
return Object.assign(result, {busy: loading}) as any;
}
const cssLoadingDotsContainer = styled('div', `
--dot-size: 10px;
display: inline-flex;

View File

@@ -416,8 +416,8 @@ export function confirmModal(
*/
export function promptModal(
title: string,
onConfirm: (text: string) => Promise<unknown>,
btnText: string,
onConfirm: (text: string) => Promise<void>,
btnText?: string,
initial?: string,
placeholder?: string,
onCancel?: () => void
@@ -429,7 +429,7 @@ export function promptModal(
const options: ISaveModalOptions = {
title,
body: txtInput,
saveLabel: btnText,
saveLabel: btnText || t('Save'),
saveFunc: () => {
// Mark that confirm was invoked.
confirmed = true;