Updated launch wizard pricing step to show portal preview

refs

- dropped the portal service in favour of using the existing `membersUtils` service
    - renamed `getPreviewUrl()` to `getPortalPreviewUrl()`
- update the iframe src to point to the portal preview url when on the pricing step
- added free/monthly/yearly checkboxes to pricing step
- update iframe src with regenerated portal preview params when making changes
This commit is contained in:
Kevin Ansfield 2021-01-28 18:41:03 +00:00 committed by Daniel Lockyer
parent fbd42ef33e
commit 7687571b12
7 changed files with 184 additions and 73 deletions

View File

@ -52,18 +52,66 @@
<GhErrorMessage @errors={{this.settings.errors}} @property="stripePlans" class="w-100 red"/>
</div>
<div class="gh-setting-first">
<div class="gh-setting-content">
<h4 class="gh-setting-title">Allow free member signup</h4>
<p class="gh-setting-desc pa0 ma0">If disabled, members can only be signed up via payment checkout or API integration</p>
<div>
<div class="mb3">
<h4 class="gh-portal-setting-title">Plans available at signup</h4>
</div>
<div class="gh-setting-action">
<div class="for-switch">
<label class="switch" for="members-allow-self-signup" {{action "toggleSelfSignup" bubbles="false"}}>
<input type="checkbox" checked={{this.settings.membersAllowFreeSignup}} class="gh-input" {{on "click" this.toggleSelfSignup}} data-test-checkbox="members-allow-self-signup">
<span class="input-toggle-component mt1"></span>
</label>
</div>
<div class="form-group mb0 for-checkbox">
<label
class="checkbox"
for="free-plan"
>
<input
type="checkbox"
checked={{this.isFreeChecked}}
id="free-plan"
name="free-plan"
disabled={{not this.settings.membersAllowFreeSignup}}
class="gh-input post-settings-featured"
{{on "click" this.toggleFreePlan}}
data-test-checkbox="featured"
>
<span class="input-toggle-component"></span>
<p>Free</p>
</label>
</div>
<div class="form-group mb0 for-checkbox">
<label
class="checkbox"
for="monthly-plan"
>
<input
type="checkbox"
id="monthly-plan"
name="monthly-plan"
checked={{this.isMonthlyChecked}}
disabled={{not this.membersUtils.isStripeEnabled}}
class="gh-input post-settings-featured"
{{on "click" this.toggleMonthlyPlan}}
data-test-checkbox="featured"
>
<span class="input-toggle-component"></span>
<p>Monthly</p>
</label>
</div>
<div class="form-group mb0 for-checkbox">
<label
class="checkbox"
for="yearly-plan"
>
<input
type="checkbox"
id="yearly-plan"
name="yearly-plan"
checked={{this.isYearlyChecked}}
disabled={{not this.membersUtils.isStripeEnabled}}
class="gh-input post-settings-featured"
{{on "click" this.toggleYearlyPlan}}
data-test-checkbox="featured"
>
<span class="input-toggle-component"></span>
<p>Yearly</p>
</label>
</div>
</div>

View File

@ -6,6 +6,8 @@ import {task} from 'ember-concurrency-decorators';
import {tracked} from '@glimmer/tracking';
export default class GhLaunchWizardSetPricingComponent extends Component {
@service config;
@service membersUtils;
@service settings;
currencies = CURRENCIES;
@ -34,9 +36,30 @@ export default class GhLaunchWizardSetPricingComponent extends Component {
return this.currencies.findBy('value', this.stripePlans.monthly.currency);
}
get isFreeChecked() {
const allowedPlans = this.settings.get('portalPlans') || [];
return (this.settings.get('membersAllowFreeSignup') && allowedPlans.includes('free'));
}
get isMonthlyChecked() {
const allowedPlans = this.settings.get('portalPlans') || [];
return (this.membersUtils.isStripeEnabled && allowedPlans.includes('monthly'));
}
get isYearlyChecked() {
const allowedPlans = this.settings.get('portalPlans') || [];
return (this.membersUtils.isStripeEnabled && allowedPlans.includes('yearly'));
}
constructor() {
super(...arguments);
this.updatePreviewUrl();
}
willDestroy() {
// clear any unsaved settings changes when going back/forward/closing
this.settings.rollbackAttributes();
this.args.updatePreview('');
}
@action
@ -66,11 +89,22 @@ export default class GhLaunchWizardSetPricingComponent extends Component {
}
this.settings.set('stripePlans', updatedPlans);
this.updatePreviewUrl();
}
@action
toggleSelfSignup() {
this.settings.set('membersAllowFreeSignup', !this.settings.get('membersAllowFreeSignup'));
toggleFreePlan(event) {
this.updateAllowedPlan('free', event.target.checked);
}
@action
toggleMonthlyPlan(event) {
this.updateAllowedPlan('monthly', event.target.checked);
}
@action
toggleYearlyPlan(event) {
this.updateAllowedPlan('yearly', event.target.checked);
}
@action
@ -109,6 +143,7 @@ export default class GhLaunchWizardSetPricingComponent extends Component {
});
this.settings.set('stripePlans', updatedPlans);
this.updatePreviewUrl();
} catch (err) {
this.settings.errors.add('stripePlans', err.message);
} finally {
@ -127,4 +162,31 @@ export default class GhLaunchWizardSetPricingComponent extends Component {
yield this.settings.save();
this.args.nextStep();
}
updateAllowedPlan(plan, isChecked) {
const allowedPlans = this.settings.get('portalPlans') || [];
if (!isChecked) {
this.settings.set('portalPlans', allowedPlans.filter(p => p !== plan));
} else {
allowedPlans.push(plan);
this.settings.set('portalPlans', [...allowedPlans]);
}
this.updatePreviewUrl();
}
updatePreviewUrl() {
const options = {
disableBackground: true,
currency: this.selectedCurrency.value,
monthlyPrice: this.stripePlans.monthly.amount,
yearlyPrice: this.stripePlans.yearly.amount,
isMonthly: this.isMonthlyChecked,
isYearly: this.isYearlyChecked,
isFree: this.isFreeChecked
};
const url = this.membersUtils.getPortalPreviewUrl(options);
this.args.updatePreview(url);
}
}

View File

@ -1,4 +1,4 @@
<iframe id="site-frame" class="site-frame {{this.classNames}}" src="{{this.srcUrl}}" frameborder="0" allowtransparency="true" ...attributes></iframe>
<iframe id="site-frame" class="site-frame {{this.classNames}}" src={{this.srcUrl}} frameborder="0" allowtransparency="true" ...attributes></iframe>
<style>
.site-frame {

View File

@ -35,7 +35,6 @@ export const ICON_MAPPING = [
export default ModalComponent.extend({
config: service(),
membersUtils: service(),
portal: service(),
settings: service(),
page: 'signup',
@ -73,7 +72,7 @@ export default ModalComponent.extend({
portalPreviewUrl: computed('buttonIcon', 'page', 'isFreeChecked', 'isMonthlyChecked', 'isYearlyChecked', 'settings.{portalName,portalButton,portalButtonSignupText,portalButtonStyle,accentColor}', function () {
const options = this.getProperties(['buttonIcon', 'page', 'isFreeChecked', 'isMonthlyChecked', 'isYearlyChecked']);
return this.portal.getPreviewUrl(options);
return this.membersUtils.getPortalPreviewUrl(options);
}),
showIconSetting: computed('selectedButtonStyle', function () {

View File

@ -75,6 +75,7 @@ export default class LaunchController extends Controller {
@action
updatePreview(url) {
console.log({url});
this.previewSrc = url;
}

View File

@ -1,9 +1,9 @@
import Service from '@ember/service';
import {ICON_MAPPING} from 'ghost-admin/components/modal-portal-settings';
import {inject as service} from '@ember/service';
export default class MembersUtilsService extends Service {
@service settings;
@service config;
@service settings;
get isStripeEnabled() {
const stripeDirect = this.config.get('stripeDirect');
@ -17,4 +17,60 @@ export default class MembersUtilsService extends Service {
return hasConnectKeys || hasDirectKeys;
}
getPortalPreviewUrl(args) {
let {
disableBackground,
buttonIcon,
page = 'signup',
isFree = true,
isMonthly = true,
isYearly = true,
monthlyPrice,
yearlyPrice,
currency
} = args;
if (!buttonIcon) {
const defaultIconKeys = ICON_MAPPING.map(icon => icon.value);
buttonIcon = this.settings.get('portalButtonIcon') || defaultIconKeys[0];
}
const baseUrl = this.config.get('blogUrl');
const portalBase = '/#/portal/preview';
const settingsParam = new URLSearchParams();
const signupButtonText = this.settings.get('portalButtonSignupText') || '';
settingsParam.append('button', this.settings.get('portalButton'));
settingsParam.append('name', this.settings.get('portalName'));
settingsParam.append('isFree', isFree);
settingsParam.append('isMonthly', isMonthly);
settingsParam.append('isYearly', isYearly);
settingsParam.append('page', page);
settingsParam.append('buttonIcon', encodeURIComponent(buttonIcon));
settingsParam.append('signupButtonText', encodeURIComponent(signupButtonText));
if (this.settings.get('accentColor') === '' || this.settings.get('accentColor')) {
settingsParam.append('accentColor', encodeURIComponent(`${this.settings.get('accentColor')}`));
}
if (this.settings.get('portalButtonStyle')) {
settingsParam.append('buttonStyle', encodeURIComponent(this.settings.get('portalButtonStyle')));
}
if (monthlyPrice) {
settingsParam.append('monthlyPrice', monthlyPrice);
}
if (yearlyPrice) {
settingsParam.append('yearlyPrice', monthlyPrice);
}
if (currency) {
settingsParam.append('currency', currency);
}
if (disableBackground) {
settingsParam.append('disableBackground', true);
}
return `${baseUrl}${portalBase}?${settingsParam.toString()}`;
}
}

View File

@ -1,55 +0,0 @@
import Service from '@ember/service';
import {ICON_MAPPING} from 'ghost-admin/components/modal-portal-settings';
import {inject as service} from '@ember/service';
export default class PortalService extends Service {
@service config;
@service settings;
getPreviewUrl(args) {
let {
buttonIcon,
page = 'signup',
isFreeChecked = true,
isMonthlyChecked = true,
isYearlyChecked = true,
monthlyPrice,
yearlyPrice
} = args;
if (!buttonIcon) {
const defaultIconKeys = ICON_MAPPING.map(icon => icon.value);
buttonIcon = this.settings.get('portalButtonIcon') || defaultIconKeys[0];
}
const baseUrl = this.config.get('blogUrl');
const portalBase = '/#/portal/preview';
const settingsParam = new URLSearchParams();
const signupButtonText = this.settings.get('portalButtonSignupText') || '';
settingsParam.append('button', this.settings.get('portalButton'));
settingsParam.append('name', this.settings.get('portalName'));
settingsParam.append('isFree', isFreeChecked);
settingsParam.append('isMonthly', isMonthlyChecked);
settingsParam.append('isYearly', isYearlyChecked);
settingsParam.append('page', page);
settingsParam.append('buttonIcon', encodeURIComponent(buttonIcon));
settingsParam.append('signupButtonText', encodeURIComponent(signupButtonText));
if (this.settings.get('accentColor') === '' || this.settings.get('accentColor')) {
settingsParam.append('accentColor', encodeURIComponent(`${this.settings.get('accentColor')}`));
}
if (this.settings.get('portalButtonStyle')) {
settingsParam.append('buttonStyle', encodeURIComponent(this.settings.get('portalButtonStyle')));
}
if (monthlyPrice) {
settingsParam.append('monthlyPrice', monthlyPrice);
}
if (yearlyPrice) {
settingsParam.append('yearlyPrice', monthlyPrice);
}
return `${baseUrl}${portalBase}?${settingsParam.toString()}`;
}
}