mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
(core) add a way to change subdomain in billing pages
Summary: This adds an `updateDomain` billing task that allows editing the subdomain (and the org name, which is also editable with the address). A warning is shown that changing the subdomain will mean that saved links need updating. Test Plan: added test Reviewers: dsagal Reviewed By: dsagal Differential Revision: https://phab.getgrist.com/D2988
This commit is contained in:
parent
d83d734b75
commit
9f25a96d18
@ -175,17 +175,22 @@ export class BillingModelImpl extends Disposable implements BillingModel {
|
|||||||
}
|
}
|
||||||
// If there is an org update, re-initialize the org in the client.
|
// If there is an org update, re-initialize the org in the client.
|
||||||
if (newSettings) { this._appModel.topAppModel.initialize(); }
|
if (newSettings) { this._appModel.topAppModel.initialize(); }
|
||||||
} else if (task === 'signUpLite') {
|
} else if (task === 'signUpLite' || task === 'updateDomain') {
|
||||||
// This is a sign up variant where payment info is handled externally.
|
|
||||||
// All that can change here is company name, and domain.
|
// All that can change here is company name, and domain.
|
||||||
const org = this._appModel.currentOrg;
|
const org = this._appModel.currentOrg;
|
||||||
const name = formData.settings && formData.settings.name;
|
const name = formData.settings && formData.settings.name;
|
||||||
const domain = formData.settings && formData.settings.domain;
|
const domain = formData.settings && formData.settings.domain;
|
||||||
const newSettings = org && (name !== org.name || domain !== org.domain) && formData.settings;
|
const newDomain = domain !== org?.domain;
|
||||||
|
const newSettings = org && (name !== org.name || newDomain) && formData.settings;
|
||||||
// If the address or settings have a new value, run the update.
|
// If the address or settings have a new value, run the update.
|
||||||
if (newSettings) {
|
if (newSettings) {
|
||||||
await this._billingAPI.updateAddress(undefined, newSettings || undefined);
|
await this._billingAPI.updateAddress(undefined, newSettings || undefined);
|
||||||
}
|
}
|
||||||
|
// If the domain has changed, should redirect page.
|
||||||
|
if (newDomain) {
|
||||||
|
window.location.assign(urlState().makeUrl({ org: domain, billing: 'billing', params: undefined }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
// If there is an org update, re-initialize the org in the client.
|
// If there is an org update, re-initialize the org in the client.
|
||||||
if (newSettings) { this._appModel.topAppModel.initialize(); }
|
if (newSettings) { this._appModel.topAppModel.initialize(); }
|
||||||
} else {
|
} else {
|
||||||
|
@ -389,11 +389,11 @@ class BillingSettingsForm extends BillingSubForm {
|
|||||||
|
|
||||||
public buildDom() {
|
public buildDom() {
|
||||||
const noEditAccess = Boolean(this._org && !roles.canEdit(this._org.access));
|
const noEditAccess = Boolean(this._org && !roles.canEdit(this._org.access));
|
||||||
|
const hasDomain = Boolean(this._options.autofill?.domain);
|
||||||
return css.paymentBlock(
|
return css.paymentBlock(
|
||||||
this._options.showHeader ? css.paymentSubHeader('Team Site') : null,
|
this._options.showHeader ? css.paymentSubHeader('Team Site') : null,
|
||||||
css.paymentRow(
|
css.paymentRow(
|
||||||
css.paymentField(
|
css.paymentField(
|
||||||
css.paymentLabel('Company Name'),
|
|
||||||
this.billingInput(this._name,
|
this.billingInput(this._name,
|
||||||
dom.boolAttr('disabled', () => noEditAccess),
|
dom.boolAttr('disabled', () => noEditAccess),
|
||||||
testId('settings-name')
|
testId('settings-name')
|
||||||
@ -410,11 +410,10 @@ class BillingSettingsForm extends BillingSubForm {
|
|||||||
dom.boolAttr('disabled', () => noEditAccess),
|
dom.boolAttr('disabled', () => noEditAccess),
|
||||||
testId('settings-domain')
|
testId('settings-domain')
|
||||||
),
|
),
|
||||||
// Note that we already do not allow editing the domain after it is initially set
|
|
||||||
// anyway, this is just here for consistency.
|
|
||||||
noEditAccess ? css.paymentFieldInfo('Organization edit access is required',
|
noEditAccess ? css.paymentFieldInfo('Organization edit access is required',
|
||||||
testId('settings-domain-info')
|
testId('settings-domain-info')
|
||||||
) : null
|
) : null,
|
||||||
|
hasDomain ? css.paymentFieldDanger('Any saved links will need updating if the URL changes') : null,
|
||||||
),
|
),
|
||||||
css.paymentField({style: 'flex: 0 1 0;'},
|
css.paymentField({style: 'flex: 0 1 0;'},
|
||||||
css.inputHintLabel('.getgrist.com')
|
css.inputHintLabel('.getgrist.com')
|
||||||
@ -442,6 +441,8 @@ class BillingSettingsForm extends BillingSubForm {
|
|||||||
|
|
||||||
// Throws if the entered domain contains any invalid characters or is already taken.
|
// Throws if the entered domain contains any invalid characters or is already taken.
|
||||||
private async _verifyDomain(domain: string): Promise<void> {
|
private async _verifyDomain(domain: string): Promise<void> {
|
||||||
|
// OK to retain current domain.
|
||||||
|
if (domain === this._options.autofill?.domain) { return; }
|
||||||
checkSubdomainValidity(domain);
|
checkSubdomainValidity(domain);
|
||||||
const isAvailable = await this._isDomainAvailable(domain);
|
const isAvailable = await this._isDomainAvailable(domain);
|
||||||
if (!isAvailable) { throw new Error('Domain is already taken.'); }
|
if (!isAvailable) { throw new Error('Domain is already taken.'); }
|
||||||
|
@ -26,7 +26,8 @@ const taskActions = {
|
|||||||
addCard: 'Add Payment Method',
|
addCard: 'Add Payment Method',
|
||||||
updateCard: 'Update Payment Method',
|
updateCard: 'Update Payment Method',
|
||||||
updateAddress: 'Update Address',
|
updateAddress: 'Update Address',
|
||||||
signUpLite: 'Complete Sign Up'
|
signUpLite: 'Complete Sign Up',
|
||||||
|
updateDomain: 'Update Name',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -357,7 +358,10 @@ export class BillingPage extends Disposable {
|
|||||||
|
|
||||||
private _buildBillingForm(org: Organization|null, address: IBillingAddress|null, task: BillingTask) {
|
private _buildBillingForm(org: Organization|null, address: IBillingAddress|null, task: BillingTask) {
|
||||||
const isSharedOrg = org && org.billingAccount && !org.billingAccount.individual;
|
const isSharedOrg = org && org.billingAccount && !org.billingAccount.individual;
|
||||||
const currentSettings = isSharedOrg ? { name: org!.name } : this._formData.settings;
|
const currentSettings = isSharedOrg ? {
|
||||||
|
name: org!.name,
|
||||||
|
domain: org?.domain?.startsWith('o-') ? undefined : org?.domain || undefined,
|
||||||
|
} : this._formData.settings;
|
||||||
const currentAddress = address || this._formData.address;
|
const currentAddress = address || this._formData.address;
|
||||||
const pageText = taskActions[task];
|
const pageText = taskActions[task];
|
||||||
// If there is an immediate charge required, require re-entering the card info.
|
// If there is an immediate charge required, require re-entering the card info.
|
||||||
@ -365,8 +369,8 @@ export class BillingPage extends Disposable {
|
|||||||
this._form = new BillingForm(org, (...args) => this._model.isDomainAvailable(...args), {
|
this._form = new BillingForm(org, (...args) => this._model.isDomainAvailable(...args), {
|
||||||
payment: ['signUp', 'updatePlan', 'addCard', 'updateCard'].includes(task),
|
payment: ['signUp', 'updatePlan', 'addCard', 'updateCard'].includes(task),
|
||||||
address: ['signUp', 'updateAddress'].includes(task),
|
address: ['signUp', 'updateAddress'].includes(task),
|
||||||
settings: ['signUp', 'signUpLite', 'updateAddress'].includes(task),
|
settings: ['signUp', 'signUpLite', 'updateAddress', 'updateDomain'].includes(task),
|
||||||
domain: ['signUp', 'signUpLite'].includes(task)
|
domain: ['signUp', 'signUpLite', 'updateDomain'].includes(task)
|
||||||
}, { address: currentAddress, settings: currentSettings, card: this._formData.card });
|
}, { address: currentAddress, settings: currentSettings, card: this._formData.card });
|
||||||
return dom('div',
|
return dom('div',
|
||||||
dom.onDispose(() => {
|
dom.onDispose(() => {
|
||||||
@ -384,7 +388,7 @@ export class BillingPage extends Disposable {
|
|||||||
private _buildPaymentConfirmation(formData: IFormData) {
|
private _buildPaymentConfirmation(formData: IFormData) {
|
||||||
const settings = formData.settings || null;
|
const settings = formData.settings || null;
|
||||||
return [
|
return [
|
||||||
this._buildDomainSummary(settings && settings.domain),
|
this._buildDomainSummary(settings && settings.domain, false),
|
||||||
this._buildCompanySummary(settings && settings.name, formData.address || null, false),
|
this._buildCompanySummary(settings && settings.name, formData.address || null, false),
|
||||||
this._buildCardSummary(formData.card || null)
|
this._buildCardSummary(formData.card || null)
|
||||||
];
|
];
|
||||||
@ -499,9 +503,9 @@ export class BillingPage extends Disposable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildDomainSummary(domain: string|null) {
|
private _buildDomainSummary(domain: string|null, showEdit: boolean = true) {
|
||||||
const task = this._model.currentTask.get();
|
const task = this._model.currentTask.get();
|
||||||
if (task === 'signUpLite') { return null; }
|
if (task === 'signUpLite' || task === 'updateDomain') { return null; }
|
||||||
return css.summaryItem(
|
return css.summaryItem(
|
||||||
css.summaryHeader(
|
css.summaryHeader(
|
||||||
css.billingBoldText('Billing Info'),
|
css.billingBoldText('Billing Info'),
|
||||||
@ -512,7 +516,14 @@ export class BillingPage extends Disposable {
|
|||||||
dom('span', {style: 'font-weight: bold'}, domain),
|
dom('span', {style: 'font-weight: bold'}, domain),
|
||||||
`.getgrist.com`,
|
`.getgrist.com`,
|
||||||
testId('org-domain')
|
testId('org-domain')
|
||||||
)
|
),
|
||||||
|
showEdit ? css.billingTextBtn(css.billingIcon('Settings'), 'Change',
|
||||||
|
urlState().setLinkUrl({
|
||||||
|
billing: 'payment',
|
||||||
|
params: { billingTask: 'updateDomain' }
|
||||||
|
}),
|
||||||
|
testId('update-domain')
|
||||||
|
) : null
|
||||||
)
|
)
|
||||||
] : null
|
] : null
|
||||||
);
|
);
|
||||||
@ -619,6 +630,8 @@ export class BillingPage extends Disposable {
|
|||||||
return makeSummaryFeature('You are updating the default payment method');
|
return makeSummaryFeature('You are updating the default payment method');
|
||||||
} else if (task === 'updateAddress') {
|
} else if (task === 'updateAddress') {
|
||||||
return makeSummaryFeature('You are updating the company name and address');
|
return makeSummaryFeature('You are updating the company name and address');
|
||||||
|
} else if (task === 'updateDomain') {
|
||||||
|
return makeSummaryFeature('You are updating the company name and domain');
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -242,6 +242,11 @@ export const paymentFieldInfo = styled('div', `
|
|||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
export const paymentFieldDanger = styled('div', `
|
||||||
|
color: #ffa500;
|
||||||
|
margin: 10px 0;
|
||||||
|
`);
|
||||||
|
|
||||||
export const paymentSpacer = styled('div', `
|
export const paymentSpacer = styled('div', `
|
||||||
width: 38px;
|
width: 38px;
|
||||||
`);
|
`);
|
||||||
|
@ -10,7 +10,8 @@ export type BillingSubPage = typeof BillingSubPage.type;
|
|||||||
export const BillingPage = StringUnion(...BillingSubPage.values, 'billing');
|
export const BillingPage = StringUnion(...BillingSubPage.values, 'billing');
|
||||||
export type BillingPage = typeof BillingPage.type;
|
export type BillingPage = typeof BillingPage.type;
|
||||||
|
|
||||||
export const BillingTask = StringUnion('signUp', 'signUpLite', 'updatePlan', 'addCard', 'updateCard', 'updateAddress');
|
export const BillingTask = StringUnion('signUp', 'signUpLite', 'updatePlan', 'addCard',
|
||||||
|
'updateCard', 'updateAddress', 'updateDomain');
|
||||||
export type BillingTask = typeof BillingTask.type;
|
export type BillingTask = typeof BillingTask.type;
|
||||||
|
|
||||||
// Note that IBillingPlan includes selected fields from the Stripe plan object along with
|
// Note that IBillingPlan includes selected fields from the Stripe plan object along with
|
||||||
|
Loading…
Reference in New Issue
Block a user