mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-05 09:50:34 +03:00
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:
parent
fbd42ef33e
commit
7687571b12
@ -52,18 +52,66 @@
|
|||||||
<GhErrorMessage @errors={{this.settings.errors}} @property="stripePlans" class="w-100 red"/>
|
<GhErrorMessage @errors={{this.settings.errors}} @property="stripePlans" class="w-100 red"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="gh-setting-first">
|
<div>
|
||||||
<div class="gh-setting-content">
|
<div class="mb3">
|
||||||
<h4 class="gh-setting-title">Allow free member signup</h4>
|
<h4 class="gh-portal-setting-title">Plans available at 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>
|
||||||
<div class="gh-setting-action">
|
<div class="form-group mb0 for-checkbox">
|
||||||
<div class="for-switch">
|
<label
|
||||||
<label class="switch" for="members-allow-self-signup" {{action "toggleSelfSignup" bubbles="false"}}>
|
class="checkbox"
|
||||||
<input type="checkbox" checked={{this.settings.membersAllowFreeSignup}} class="gh-input" {{on "click" this.toggleSelfSignup}} data-test-checkbox="members-allow-self-signup">
|
for="free-plan"
|
||||||
<span class="input-toggle-component mt1"></span>
|
>
|
||||||
</label>
|
<input
|
||||||
</div>
|
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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ import {task} from 'ember-concurrency-decorators';
|
|||||||
import {tracked} from '@glimmer/tracking';
|
import {tracked} from '@glimmer/tracking';
|
||||||
|
|
||||||
export default class GhLaunchWizardSetPricingComponent extends Component {
|
export default class GhLaunchWizardSetPricingComponent extends Component {
|
||||||
|
@service config;
|
||||||
|
@service membersUtils;
|
||||||
@service settings;
|
@service settings;
|
||||||
|
|
||||||
currencies = CURRENCIES;
|
currencies = CURRENCIES;
|
||||||
@ -34,9 +36,30 @@ export default class GhLaunchWizardSetPricingComponent extends Component {
|
|||||||
return this.currencies.findBy('value', this.stripePlans.monthly.currency);
|
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() {
|
willDestroy() {
|
||||||
// clear any unsaved settings changes when going back/forward/closing
|
// clear any unsaved settings changes when going back/forward/closing
|
||||||
this.settings.rollbackAttributes();
|
this.settings.rollbackAttributes();
|
||||||
|
this.args.updatePreview('');
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@ -66,11 +89,22 @@ export default class GhLaunchWizardSetPricingComponent extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.settings.set('stripePlans', updatedPlans);
|
this.settings.set('stripePlans', updatedPlans);
|
||||||
|
this.updatePreviewUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleSelfSignup() {
|
toggleFreePlan(event) {
|
||||||
this.settings.set('membersAllowFreeSignup', !this.settings.get('membersAllowFreeSignup'));
|
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
|
@action
|
||||||
@ -109,6 +143,7 @@ export default class GhLaunchWizardSetPricingComponent extends Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.settings.set('stripePlans', updatedPlans);
|
this.settings.set('stripePlans', updatedPlans);
|
||||||
|
this.updatePreviewUrl();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.settings.errors.add('stripePlans', err.message);
|
this.settings.errors.add('stripePlans', err.message);
|
||||||
} finally {
|
} finally {
|
||||||
@ -127,4 +162,31 @@ export default class GhLaunchWizardSetPricingComponent extends Component {
|
|||||||
yield this.settings.save();
|
yield this.settings.save();
|
||||||
this.args.nextStep();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
<style>
|
||||||
.site-frame {
|
.site-frame {
|
||||||
|
@ -35,7 +35,6 @@ export const ICON_MAPPING = [
|
|||||||
export default ModalComponent.extend({
|
export default ModalComponent.extend({
|
||||||
config: service(),
|
config: service(),
|
||||||
membersUtils: service(),
|
membersUtils: service(),
|
||||||
portal: service(),
|
|
||||||
settings: service(),
|
settings: service(),
|
||||||
|
|
||||||
page: 'signup',
|
page: 'signup',
|
||||||
@ -73,7 +72,7 @@ export default ModalComponent.extend({
|
|||||||
|
|
||||||
portalPreviewUrl: computed('buttonIcon', 'page', 'isFreeChecked', 'isMonthlyChecked', 'isYearlyChecked', 'settings.{portalName,portalButton,portalButtonSignupText,portalButtonStyle,accentColor}', function () {
|
portalPreviewUrl: computed('buttonIcon', 'page', 'isFreeChecked', 'isMonthlyChecked', 'isYearlyChecked', 'settings.{portalName,portalButton,portalButtonSignupText,portalButtonStyle,accentColor}', function () {
|
||||||
const options = this.getProperties(['buttonIcon', 'page', 'isFreeChecked', 'isMonthlyChecked', 'isYearlyChecked']);
|
const options = this.getProperties(['buttonIcon', 'page', 'isFreeChecked', 'isMonthlyChecked', 'isYearlyChecked']);
|
||||||
return this.portal.getPreviewUrl(options);
|
return this.membersUtils.getPortalPreviewUrl(options);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
showIconSetting: computed('selectedButtonStyle', function () {
|
showIconSetting: computed('selectedButtonStyle', function () {
|
||||||
|
@ -75,6 +75,7 @@ export default class LaunchController extends Controller {
|
|||||||
|
|
||||||
@action
|
@action
|
||||||
updatePreview(url) {
|
updatePreview(url) {
|
||||||
|
console.log({url});
|
||||||
this.previewSrc = url;
|
this.previewSrc = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import Service from '@ember/service';
|
import Service from '@ember/service';
|
||||||
|
import {ICON_MAPPING} from 'ghost-admin/components/modal-portal-settings';
|
||||||
import {inject as service} from '@ember/service';
|
import {inject as service} from '@ember/service';
|
||||||
|
|
||||||
export default class MembersUtilsService extends Service {
|
export default class MembersUtilsService extends Service {
|
||||||
@service settings;
|
|
||||||
@service config;
|
@service config;
|
||||||
|
@service settings;
|
||||||
|
|
||||||
get isStripeEnabled() {
|
get isStripeEnabled() {
|
||||||
const stripeDirect = this.config.get('stripeDirect');
|
const stripeDirect = this.config.get('stripeDirect');
|
||||||
@ -17,4 +17,60 @@ export default class MembersUtilsService extends Service {
|
|||||||
|
|
||||||
return hasConnectKeys || hasDirectKeys;
|
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()}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()}`;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user