mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-03-11 02:12:21 -05:00
Revert "Added logic to load Portal when Tips & Donations are enabled" (#17655)
no issue
- this reverts commit b95c8275f2
- browser tests needs to be fixed
This commit is contained in:
parent
b95c8275f2
commit
6cfa4bb6e8
11 changed files with 62 additions and 244 deletions
|
@ -9,7 +9,7 @@ import {ReactComponent as ButtonIcon3} from '../images/icons/button-icon-3.svg';
|
|||
import {ReactComponent as ButtonIcon4} from '../images/icons/button-icon-4.svg';
|
||||
import {ReactComponent as ButtonIcon5} from '../images/icons/button-icon-5.svg';
|
||||
import TriggerButtonStyle from './TriggerButton.styles';
|
||||
import {isInviteOnlySite, isSigninAllowed} from '../utils/helpers';
|
||||
import {isInviteOnlySite} from '../utils/helpers';
|
||||
import {hasMode} from '../utils/check-mode';
|
||||
|
||||
const ICON_MAPPING = {
|
||||
|
@ -164,21 +164,12 @@ class TriggerButtonContent extends React.Component {
|
|||
|
||||
onToggle() {
|
||||
const {showPopup, member, site} = this.context;
|
||||
|
||||
if (showPopup) {
|
||||
this.context.onAction('closePopup');
|
||||
return;
|
||||
}
|
||||
|
||||
if (member) {
|
||||
this.context.onAction('openPopup', {page: 'accountHome'});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSigninAllowed({site})) {
|
||||
const page = isInviteOnlySite({site}) ? 'signin' : 'signup';
|
||||
} else {
|
||||
const loggedOutPage = isInviteOnlySite({site}) ? 'signin' : 'signup';
|
||||
const page = member ? 'accountHome' : loggedOutPage;
|
||||
this.context.onAction('openPopup', {page});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,11 +240,10 @@ export default class TriggerButton extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const site = this.context.site;
|
||||
const {portal_button: portalButton} = site;
|
||||
const {portal_button: portalButton} = this.context.site;
|
||||
const {showPopup} = this.context;
|
||||
|
||||
if (!portalButton || !isSigninAllowed({site}) || hasMode(['offerPreview'])) {
|
||||
if (!portalButton || hasMode(['offerPreview'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,18 +4,12 @@ import {getSupportAddress} from '../../../utils/helpers';
|
|||
|
||||
import AccountFooter from './components/AccountFooter';
|
||||
import AccountMain from './components/AccountMain';
|
||||
import {isSigninAllowed} from '../../../utils/helpers';
|
||||
|
||||
export default class AccountHomePage extends React.Component {
|
||||
static contextType = AppContext;
|
||||
|
||||
componentDidMount() {
|
||||
const {member, site} = this.context;
|
||||
|
||||
if (!isSigninAllowed({site})) {
|
||||
this.context.onAction('signout');
|
||||
}
|
||||
|
||||
const {member} = this.context;
|
||||
if (!member) {
|
||||
this.context.onAction('switchPage', {
|
||||
page: 'signin',
|
||||
|
@ -37,9 +31,6 @@ export default class AccountHomePage extends React.Component {
|
|||
if (!member) {
|
||||
return null;
|
||||
}
|
||||
if (!isSigninAllowed({site})) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className='gh-portal-account-wrapper'>
|
||||
<AccountMain />
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import AppContext from '../../../../AppContext';
|
||||
import ActionButton from '../../../common/ActionButton';
|
||||
import {isSignupAllowed} from '../../../../utils/helpers';
|
||||
import {hasOnlyFreePlan} from '../../../../utils/helpers';
|
||||
import {useContext} from 'react';
|
||||
|
||||
const SubscribeButton = () => {
|
||||
const {site, action, brandColor, onAction, t} = useContext(AppContext);
|
||||
const {is_stripe_configured: isStripeConfigured} = site;
|
||||
|
||||
if (!isSignupAllowed({site})) {
|
||||
if (!isStripeConfigured || hasOnlyFreePlan({site})) {
|
||||
return null;
|
||||
}
|
||||
const isRunning = ['checkoutPlan:running'].includes(action);
|
||||
|
|
|
@ -5,8 +5,6 @@ import CloseButton from '../common/CloseButton';
|
|||
import AppContext from '../../AppContext';
|
||||
import InputForm from '../common/InputForm';
|
||||
import {ValidateInputForm} from '../../utils/form';
|
||||
import {isSigninAllowed} from '../../utils/helpers';
|
||||
import {ReactComponent as InvitationIcon} from '../../images/icons/invitation.svg';
|
||||
|
||||
export default class SigninPage extends React.Component {
|
||||
static contextType = AppContext;
|
||||
|
@ -118,23 +116,6 @@ export default class SigninPage extends React.Component {
|
|||
}
|
||||
|
||||
renderForm() {
|
||||
const {site, t} = this.context;
|
||||
|
||||
if (!isSigninAllowed({site})) {
|
||||
return (
|
||||
<section>
|
||||
<div className='gh-portal-section'>
|
||||
<p
|
||||
className='gh-portal-members-disabled-notification'
|
||||
data-testid="members-disabled-notification-text"
|
||||
>
|
||||
{t('Memberships unavailable, contact the owner for access.')}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className='gh-portal-section'>
|
||||
|
@ -144,52 +125,32 @@ export default class SigninPage extends React.Component {
|
|||
onKeyDown={(e, field) => this.onKeyDown(e, field)}
|
||||
/>
|
||||
</div>
|
||||
<footer className='gh-portal-signin-footer'>
|
||||
{this.renderSubmitButton()}
|
||||
{this.renderSignupMessage()}
|
||||
</footer>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
renderSiteIcon() {
|
||||
const iconStyle = {};
|
||||
const {site} = this.context;
|
||||
const siteIcon = site.icon;
|
||||
renderSiteLogo() {
|
||||
const siteLogo = this.context.site.icon;
|
||||
|
||||
if (siteIcon) {
|
||||
iconStyle.backgroundImage = `url(${siteIcon})`;
|
||||
const logoStyle = {};
|
||||
|
||||
if (siteLogo) {
|
||||
logoStyle.backgroundImage = `url(${siteLogo})`;
|
||||
return (
|
||||
<img className='gh-portal-signup-logo' src={siteIcon} alt={this.context.site.title} />
|
||||
);
|
||||
} else if (!isSigninAllowed({site})) {
|
||||
return (
|
||||
<InvitationIcon className='gh-portal-icon gh-portal-icon-invitation' />
|
||||
<img className='gh-portal-signup-logo' src={siteLogo} alt={this.context.site.title} />
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
renderSiteTitle() {
|
||||
const {site, t} = this.context;
|
||||
const siteTitle = site.title;
|
||||
|
||||
if (!isSigninAllowed({site})) {
|
||||
return (
|
||||
<h1 className='gh-portal-main-title'>{siteTitle}</h1>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<h1 className='gh-portal-main-title'>{t('Sign in')}</h1>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderFormHeader() {
|
||||
// const siteTitle = this.context.site.title || 'Site Title';
|
||||
const {t} = this.context;
|
||||
|
||||
return (
|
||||
<header className='gh-portal-signin-header'>
|
||||
{this.renderSiteIcon()}
|
||||
{this.renderSiteTitle()}
|
||||
{this.renderSiteLogo()}
|
||||
<h1 className="gh-portal-main-title">{t('Sign in')}</h1>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
@ -197,12 +158,19 @@ export default class SigninPage extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<>
|
||||
{/* <div className='gh-portal-back-sitetitle'>
|
||||
<SiteTitleBackButton />
|
||||
</div> */}
|
||||
<CloseButton />
|
||||
<div className='gh-portal-logged-out-form-container'>
|
||||
<div className='gh-portal-content signin'>
|
||||
{this.renderFormHeader()}
|
||||
{this.renderForm()}
|
||||
</div>
|
||||
<footer className='gh-portal-signin-footer'>
|
||||
{this.renderSubmitButton()}
|
||||
{this.renderSignupMessage()}
|
||||
</footer>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,30 +1,18 @@
|
|||
import {render, fireEvent, getByTestId} from '../../utils/test-utils';
|
||||
import {render, fireEvent} from '../../utils/test-utils';
|
||||
import SigninPage from './SigninPage';
|
||||
import {getSiteData} from '../../utils/fixtures-generator';
|
||||
|
||||
const setup = (overrides) => {
|
||||
const setup = () => {
|
||||
const {mockOnActionFn, ...utils} = render(
|
||||
<SigninPage />,
|
||||
{
|
||||
overrideContext: {
|
||||
member: null,
|
||||
...overrides
|
||||
member: null
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let emailInput;
|
||||
let submitButton;
|
||||
let signupButton;
|
||||
|
||||
try {
|
||||
emailInput = utils.getByLabelText(/email/i);
|
||||
submitButton = utils.queryByRole('button', {name: 'Continue'});
|
||||
signupButton = utils.queryByRole('button', {name: 'Sign up'});
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
const emailInput = utils.getByLabelText(/email/i);
|
||||
const submitButton = utils.queryByRole('button', {name: 'Continue'});
|
||||
const signupButton = utils.queryByRole('button', {name: 'Sign up'});
|
||||
return {
|
||||
emailInput,
|
||||
submitButton,
|
||||
|
@ -59,17 +47,4 @@ describe('SigninPage', () => {
|
|||
fireEvent.click(signupButton);
|
||||
expect(mockOnActionFn).toHaveBeenCalledWith('switchPage', {page: 'signup'});
|
||||
});
|
||||
|
||||
describe('when members are disabled', () => {
|
||||
test('renders an informative message', () => {
|
||||
setup({
|
||||
site: getSiteData({
|
||||
membersSignupAccess: 'none'
|
||||
})
|
||||
});
|
||||
|
||||
const message = getByTestId(document.body, 'members-disabled-notification-text');
|
||||
expect(message).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@ import NewsletterSelectionPage from './NewsletterSelectionPage';
|
|||
import ProductsSection from '../common/ProductsSection';
|
||||
import InputForm from '../common/InputForm';
|
||||
import {ValidateInputForm} from '../../utils/form';
|
||||
import {getSiteProducts, getSitePrices, hasOnlyFreePlan, isInviteOnlySite, freeHasBenefitsOrDescription, hasOnlyFreeProduct, getFreeProductBenefits, getFreeTierDescription, hasFreeProductPrice, hasMultipleNewsletters, hasFreeTrialTier, isSignupAllowed} from '../../utils/helpers';
|
||||
import {getSiteProducts, getSitePrices, hasOnlyFreePlan, isInviteOnlySite, freeHasBenefitsOrDescription, hasOnlyFreeProduct, getFreeProductBenefits, getFreeTierDescription, hasFreeProductPrice, hasMultipleNewsletters, hasFreeTrialTier} from '../../utils/helpers';
|
||||
import {ReactComponent as InvitationIcon} from '../../images/icons/invitation.svg';
|
||||
|
||||
export const SignupPageStyles = `
|
||||
|
@ -171,7 +171,7 @@ footer.gh-portal-signup-footer.invite-only .gh-portal-signup-message {
|
|||
margin-top: 0;
|
||||
}
|
||||
|
||||
.gh-portal-invite-only-notification, .gh-portal-members-disabled-notification {
|
||||
.gh-portal-invite-only-notification {
|
||||
margin: 8px 32px 24px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
|
@ -670,21 +670,6 @@ class SignupPage extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
if (!isSignupAllowed({site})) {
|
||||
return (
|
||||
<section>
|
||||
<div className='gh-portal-section'>
|
||||
<p
|
||||
className='gh-portal-members-disabled-notification'
|
||||
data-testid="members-disabled-notification-text"
|
||||
>
|
||||
{t('Memberships unavailable, contact the owner for access.')}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const freeBenefits = getFreeProductBenefits({site});
|
||||
const freeDescription = getFreeTierDescription({site});
|
||||
const showOnlyFree = pageQuery === 'free' && hasFreeProductPrice({site});
|
||||
|
@ -731,22 +716,22 @@ class SignupPage extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
renderSiteIcon() {
|
||||
renderSiteLogo() {
|
||||
const {site, pageQuery} = this.context;
|
||||
const siteIcon = site.icon;
|
||||
|
||||
if (siteIcon) {
|
||||
const siteLogo = site.icon;
|
||||
|
||||
const logoStyle = {};
|
||||
|
||||
if (siteLogo) {
|
||||
logoStyle.backgroundImage = `url(${siteLogo})`;
|
||||
return (
|
||||
<img className='gh-portal-signup-logo' src={siteIcon} alt={site.title} />
|
||||
<img className='gh-portal-signup-logo' src={siteLogo} alt={site.title} />
|
||||
);
|
||||
} else if (isInviteOnlySite({site, pageQuery})) {
|
||||
return (
|
||||
<InvitationIcon className='gh-portal-icon gh-portal-icon-invitation' />
|
||||
);
|
||||
} else if (!isSignupAllowed({site})) {
|
||||
return (
|
||||
<InvitationIcon className='gh-portal-icon gh-portal-icon-invitation' />
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -756,7 +741,7 @@ class SignupPage extends React.Component {
|
|||
const siteTitle = site.title || '';
|
||||
return (
|
||||
<header className='gh-portal-signup-header'>
|
||||
{this.renderSiteIcon()}
|
||||
{this.renderSiteLogo()}
|
||||
<h1 className="gh-portal-main-title" data-testid='site-title-text'>{siteTitle}</h1>
|
||||
</header>
|
||||
);
|
||||
|
@ -809,6 +794,10 @@ class SignupPage extends React.Component {
|
|||
{this.renderFormHeader()}
|
||||
{this.renderForm()}
|
||||
</div>
|
||||
{/* <footer className={'gh-portal-signup-footer gh-portal-logged-out-form-container ' + footerClass}>
|
||||
{this.renderSubmitButton()}
|
||||
{this.renderLoginMessage()}
|
||||
</footer> */}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import SignupPage from './SignupPage';
|
||||
import {getFreeProduct, getProductData, getSiteData} from '../../utils/fixtures-generator';
|
||||
import {render, fireEvent, getByTestId} from '../../utils/test-utils';
|
||||
import {render, fireEvent} from '../../utils/test-utils';
|
||||
|
||||
const setup = (overrides) => {
|
||||
const {mockOnActionFn, ...utils} = render(
|
||||
|
@ -12,25 +12,12 @@ const setup = (overrides) => {
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
let emailInput;
|
||||
let nameInput;
|
||||
let submitButton;
|
||||
let chooseButton;
|
||||
let signinButton;
|
||||
let freeTrialMessage;
|
||||
|
||||
try {
|
||||
emailInput = utils.getByLabelText(/email/i);
|
||||
nameInput = utils.getByLabelText(/name/i);
|
||||
submitButton = utils.queryByRole('button', {name: 'Continue'});
|
||||
chooseButton = utils.queryAllByRole('button', {name: 'Choose'});
|
||||
signinButton = utils.queryByRole('button', {name: 'Sign in'});
|
||||
freeTrialMessage = utils.queryByText(/After a free trial ends/i);
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
const emailInput = utils.getByLabelText(/email/i);
|
||||
const nameInput = utils.getByLabelText(/name/i);
|
||||
const submitButton = utils.queryByRole('button', {name: 'Continue'});
|
||||
const chooseButton = utils.queryAllByRole('button', {name: 'Choose'});
|
||||
const signinButton = utils.queryByRole('button', {name: 'Sign in'});
|
||||
const freeTrialMessage = utils.queryByText(/After a free trial ends/i);
|
||||
return {
|
||||
nameInput,
|
||||
emailInput,
|
||||
|
@ -102,17 +89,4 @@ describe('SignupPage', () => {
|
|||
|
||||
expect(freeTrialMessage).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('when members are disabled', () => {
|
||||
test('renders an informative message', () => {
|
||||
setup({
|
||||
site: getSiteData({
|
||||
membersSignupAccess: 'none'
|
||||
})
|
||||
});
|
||||
|
||||
const message = getByTestId(document.body, 'members-disabled-notification-text');
|
||||
expect(message).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -238,14 +238,6 @@ export function isInviteOnlySite({site = {}, pageQuery = ''}) {
|
|||
return prices.length === 0 || (site && site.members_signup_access === 'invite');
|
||||
}
|
||||
|
||||
export function isSigninAllowed({site}) {
|
||||
return site?.members_signup_access === 'all' || site?.members_signup_access === 'invite';
|
||||
}
|
||||
|
||||
export function isSignupAllowed({site}) {
|
||||
return site?.members_signup_access === 'all' && (site?.is_stripe_configured || hasOnlyFreePlan({site}));
|
||||
}
|
||||
|
||||
export function hasMultipleProducts({site}) {
|
||||
const products = getAvailableProducts({site});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {getAllProductsForSite, getAvailableProducts, getCurrencySymbol, getFreeProduct, getMemberName, getMemberSubscription, getPriceFromSubscription, getPriceIdFromPageQuery, getSupportAddress, getUrlHistory, hasMultipleProducts, isActiveOffer, isInviteOnlySite, isPaidMember, isSameCurrency, transformApiTiersData, isSigninAllowed, isSignupAllowed} from './helpers';
|
||||
import {getAllProductsForSite, getAvailableProducts, getCurrencySymbol, getFreeProduct, getMemberName, getMemberSubscription, getPriceFromSubscription, getPriceIdFromPageQuery, getSupportAddress, getUrlHistory, hasMultipleProducts, isActiveOffer, isInviteOnlySite, isPaidMember, isSameCurrency, transformApiTiersData} from './helpers';
|
||||
import * as Fixtures from './fixtures-generator';
|
||||
import {site as FixturesSite, member as FixtureMember, offer as FixtureOffer, transformTierFixture as TransformFixtureTiers} from '../utils/test-fixtures';
|
||||
import {isComplimentaryMember} from '../utils/helpers';
|
||||
|
@ -137,12 +137,8 @@ describe('Helpers - ', () => {
|
|||
});
|
||||
|
||||
describe('isInviteOnlySite - ', () => {
|
||||
test('returns true for a site without plans', () => {
|
||||
const value = isInviteOnlySite({site: FixturesSite.singleTier.withoutPlans});
|
||||
expect(value).toBe(true);
|
||||
});
|
||||
test('returns true for a site with invite-only members', () => {
|
||||
const value = isInviteOnlySite({site: FixturesSite.singleTier.membersInviteOnly});
|
||||
test('returns true for invite only site', () => {
|
||||
const value = isInviteOnlySite({site: FixturesSite.singleTier.inviteOnly});
|
||||
expect(value).toBe(true);
|
||||
});
|
||||
test('returns false for non invite only site', () => {
|
||||
|
@ -151,45 +147,6 @@ describe('Helpers - ', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('isSigninAllowed - ', () => {
|
||||
test('returns true for a site with members enabled', () => {
|
||||
const value = isSigninAllowed({site: FixturesSite.singleTier.basic});
|
||||
expect(value).toBe(true);
|
||||
});
|
||||
|
||||
test('returns true for a site with invite-only members', () => {
|
||||
const value = isSigninAllowed({site: FixturesSite.singleTier.membersInviteOnly});
|
||||
expect(value).toBe(true);
|
||||
});
|
||||
|
||||
test('returns false for a site with members disabled', () => {
|
||||
const value = isSigninAllowed({site: FixturesSite.singleTier.membersDisabled});
|
||||
expect(value).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSignupAllowed - ', () => {
|
||||
test('returns true for a site with members enabled, and with Stripe configured', () => {
|
||||
const value = isSignupAllowed({site: FixturesSite.singleTier.basic});
|
||||
expect(value).toBe(true);
|
||||
});
|
||||
|
||||
test('returns true for a site with members enabled, without Stripe configured, but with only free tiers', () => {
|
||||
const value = isSignupAllowed({site: FixturesSite.singleTier.onlyFreePlanWithoutStripe});
|
||||
expect(value).toBe(true);
|
||||
});
|
||||
|
||||
test('returns false for a site with invite-only members', () => {
|
||||
const value = isSignupAllowed({site: FixturesSite.singleTier.membersInviteOnly});
|
||||
expect(value).toBe(false);
|
||||
});
|
||||
|
||||
test('returns false for a site with members disabled', () => {
|
||||
const value = isSignupAllowed({site: FixturesSite.singleTier.membersDisabled});
|
||||
expect(value).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasMultipleProducts - ', () => {
|
||||
test('returns true for multiple tier site', () => {
|
||||
const value = hasMultipleProducts({site: FixturesSite.multipleTiers.basic});
|
||||
|
|
|
@ -136,7 +136,7 @@ const baseMultiTierSite = getSiteData({
|
|||
export const site = {
|
||||
singleTier: {
|
||||
basic: baseSingleTierSite,
|
||||
withoutPlans: {
|
||||
inviteOnly: {
|
||||
...baseSingleTierSite,
|
||||
portal_plans: []
|
||||
},
|
||||
|
@ -151,23 +151,6 @@ export const site = {
|
|||
withoutName: {
|
||||
...baseSingleTierSite,
|
||||
portal_name: false
|
||||
},
|
||||
withoutStripe: {
|
||||
...baseSingleTierSite,
|
||||
is_stripe_configured: false
|
||||
},
|
||||
onlyFreePlanWithoutStripe: {
|
||||
...baseSingleTierSite,
|
||||
portal_plans: ['free'],
|
||||
is_stripe_configured: false
|
||||
},
|
||||
membersInviteOnly: {
|
||||
...baseSingleTierSite,
|
||||
members_signup_access: 'invite'
|
||||
},
|
||||
membersDisabled: {
|
||||
...baseSingleTierSite,
|
||||
members_signup_access: 'none'
|
||||
}
|
||||
},
|
||||
multipleTiers: {
|
||||
|
|
|
@ -45,11 +45,9 @@ function finaliseStructuredData(meta) {
|
|||
}
|
||||
|
||||
function getMembersHelper(data, frontendKey) {
|
||||
// Do not load Portal if both Memberships and Tips & Donations are disabled
|
||||
if (!settingsCache.get('members_enabled') && !settingsCache.get('donations_enabled')) {
|
||||
if (!settingsCache.get('members_enabled')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const {scriptUrl} = getFrontendAppConfig('portal');
|
||||
|
||||
const colorString = (_.has(data, 'site._preview') && data.site.accent_color) ? data.site.accent_color : '';
|
||||
|
|
Loading…
Add table
Reference in a new issue