mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-23 11:55:01 +03:00
Added feature flag for AdminX Offers (#18801)
closes https://github.com/TryGhost/Product/issues/4084 --- ### <samp>🤖 Generated by Copilot at 9380e1f</samp> This pull request adds a new UI for creating and managing offers for members in the admin settings, which is controlled by an alpha feature flag. It introduces new modal components for the offers UI, a new sidebar item, new routes, and a new setting group. It also updates the `labs.js` file to include the `adminXOffers` flag.
This commit is contained in:
parent
ab57071901
commit
3711260f9d
@ -23,6 +23,7 @@ const Sidebar: React.FC = () => {
|
||||
const {updateRoute} = useRouting();
|
||||
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const {isAnyTextFieldFocused} = useFocusContext();
|
||||
const hasOffers = useFeatureFlag('adminXOffers');
|
||||
|
||||
// Focus in on search field when pressing "/"
|
||||
useEffect(() => {
|
||||
@ -108,6 +109,7 @@ const Sidebar: React.FC = () => {
|
||||
<SettingNavItem icon='emailfield' keywords={membershipSearchKeywords.embedSignupForm} navid='embed-signup-form' title="Embeddable signup form" onClick={handleSectionClick} />
|
||||
{hasRecommendations && <SettingNavItem icon='heart' keywords={membershipSearchKeywords.recommendations} navid='recommendations' title="Recommendations" onClick={handleSectionClick} />}
|
||||
<SettingNavItem icon='baseline-chart' keywords={membershipSearchKeywords.analytics} navid='analytics' title="Analytics" onClick={handleSectionClick} />
|
||||
{hasOffers && <SettingNavItem icon='ellipsis' keywords={membershipSearchKeywords.offers} navid='offers' title="Offers" onClick={handleSectionClick} />}
|
||||
</SettingNavSection>
|
||||
|
||||
<SettingNavSection keywords={Object.values(emailSearchKeywords).flat()} title="Email newsletter">
|
||||
|
@ -62,6 +62,9 @@ const modalPaths: {[key: string]: ModalName} = {
|
||||
'recommendations/edit': 'EditRecommendationModal',
|
||||
'announcement-bar/edit': 'AnnouncementBarModal',
|
||||
'embed-signup-form/show': 'EmbedSignupFormModal',
|
||||
'offers/edit': 'OffersModal',
|
||||
'offers/new': 'AddOfferModal',
|
||||
'offers/:id': 'EditOfferModal',
|
||||
about: 'AboutModal'
|
||||
};
|
||||
|
||||
|
@ -4,11 +4,13 @@ import type {RoutingModalProps} from '../RoutingProvider';
|
||||
import AboutModal from '../../settings/general/About';
|
||||
import AddIntegrationModal from '../../settings/advanced/integrations/AddIntegrationModal';
|
||||
import AddNewsletterModal from '../../settings/email/newsletters/AddNewsletterModal';
|
||||
import AddOfferModal from '../../settings/membership/offers/AddOfferModal';
|
||||
import AddRecommendationModal from '../../settings/membership/recommendations/AddRecommendationModal';
|
||||
import AmpModal from '../../settings/advanced/integrations/AmpModal';
|
||||
import AnnouncementBarModal from '../../settings/site/AnnouncementBarModal';
|
||||
import CustomIntegrationModal from '../../settings/advanced/integrations/CustomIntegrationModal';
|
||||
import DesignAndThemeModal from '../../settings/site/DesignAndThemeModal';
|
||||
import EditOfferModal from '../../settings/membership/offers/EditOfferModal';
|
||||
import EditRecommendationModal from '../../settings/membership/recommendations/EditRecommendationModal';
|
||||
import EmbedSignupFormModal from '../../settings/membership/embedSignup/EmbedSignupFormModal';
|
||||
import FirstpromoterModal from '../../settings/advanced/integrations/FirstPromoterModal';
|
||||
@ -16,6 +18,7 @@ import HistoryModal from '../../settings/advanced/HistoryModal';
|
||||
import InviteUserModal from '../../settings/general/InviteUserModal';
|
||||
import NavigationModal from '../../settings/site/NavigationModal';
|
||||
import NewsletterDetailModal from '../../settings/email/newsletters/NewsletterDetailModal';
|
||||
import OffersModal from '../../settings/membership/offers/OffersModal';
|
||||
import PinturaModal from '../../settings/advanced/integrations/PinturaModal';
|
||||
import PortalModal from '../../settings/membership/portal/PortalModal';
|
||||
import SlackModal from '../../settings/advanced/integrations/SlackModal';
|
||||
@ -48,6 +51,9 @@ const modals = {
|
||||
ZapierModal,
|
||||
AnnouncementBarModal,
|
||||
EmbedSignupFormModal,
|
||||
OffersModal,
|
||||
AddOfferModal,
|
||||
EditOfferModal,
|
||||
AboutModal
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} satisfies {[key: string]: ModalComponent<any>};
|
||||
|
@ -55,6 +55,10 @@ const features = [{
|
||||
title: 'Editor emoji picker',
|
||||
description: <>Trigger an emoji picker when typing <code>{':{search}'}</code> in the editor</>,
|
||||
flag: 'editorEmojiPicker'
|
||||
},{
|
||||
title: 'AdminX Offers',
|
||||
description: 'Enables the new offers UI in AdminX settings',
|
||||
flag: 'adminXOffers'
|
||||
}];
|
||||
|
||||
const AlphaFeatures: React.FC = () => {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Access from './Access';
|
||||
import Analytics from './Analytics';
|
||||
import EmbedSignupForm from './embedSignup/EmbedSignupForm';
|
||||
import Offers from './Offers';
|
||||
import Portal from './Portal';
|
||||
import React from 'react';
|
||||
import Recommendations from './Recommendations';
|
||||
@ -16,12 +17,14 @@ export const searchKeywords = {
|
||||
tips: ['tip', 'donation', 'one time', 'payment'],
|
||||
embedSignupForm: ['embeddable signup form', 'embeddable form', 'embeddable sign up form', 'embeddable sign up'],
|
||||
recommendations: ['recommendations', 'recommend', 'blogroll'],
|
||||
analytics: ['analytics', 'tracking', 'privacy', 'membership']
|
||||
analytics: ['analytics', 'tracking', 'privacy', 'membership'],
|
||||
offers: ['offers', 'discounts', 'coupons', 'promotions']
|
||||
};
|
||||
|
||||
const MembershipSettings: React.FC = () => {
|
||||
const hasTipsAndDonations = useFeatureFlag('tipsAndDonations');
|
||||
const hasRecommendations = useFeatureFlag('recommendations');
|
||||
const hasOffers = useFeatureFlag('adminXOffers');
|
||||
|
||||
return (
|
||||
<SettingSection keywords={Object.values(searchKeywords).flat()} title='Membership'>
|
||||
@ -32,6 +35,7 @@ const MembershipSettings: React.FC = () => {
|
||||
<EmbedSignupForm keywords={searchKeywords.embedSignupForm} />
|
||||
{hasRecommendations && <Recommendations keywords={searchKeywords.recommendations} />}
|
||||
<Analytics keywords={searchKeywords.analytics} />
|
||||
{hasOffers && <Offers keywords={searchKeywords.offers} />}
|
||||
</SettingSection>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,29 @@
|
||||
import Button from '../../../admin-x-ds/global/Button';
|
||||
import React from 'react';
|
||||
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import useRouting from '../../../hooks/useRouting';
|
||||
import {checkStripeEnabled} from '../../../api/settings';
|
||||
import {useGlobalData} from '../../providers/GlobalDataProvider';
|
||||
import {withErrorBoundary} from '../../../admin-x-ds/global/ErrorBoundary';
|
||||
|
||||
const Offers: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
const {updateRoute} = useRouting();
|
||||
const {settings, config} = useGlobalData();
|
||||
|
||||
const openModal = () => {
|
||||
updateRoute('offers/edit');
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingGroup
|
||||
customButtons={<Button color='green' disabled={!checkStripeEnabled(settings, config)} label='Manage offers' link linkWithPadding onClick={openModal}/>}
|
||||
description='Grow your audience by providing fixed or percentage discounts. [Learn more]'
|
||||
keywords={keywords}
|
||||
navid='offers'
|
||||
testId='offers'
|
||||
title='Offers'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default withErrorBoundary(Offers, 'Portal settings');
|
@ -0,0 +1,22 @@
|
||||
import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
||||
import useFeatureFlag from '../../../../hooks/useFeatureFlag';
|
||||
import useRouting from '../../../../hooks/useRouting';
|
||||
import {PreviewModalContent} from '../../../../admin-x-ds/global/modal/PreviewModal';
|
||||
import {useEffect} from 'react';
|
||||
|
||||
const AddOfferModal = () => {
|
||||
const modal = useModal();
|
||||
const {updateRoute} = useRouting();
|
||||
const hasOffers = useFeatureFlag('adminXOffers');
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasOffers) {
|
||||
modal.remove();
|
||||
updateRoute('');
|
||||
}
|
||||
}, [hasOffers, modal, updateRoute]);
|
||||
|
||||
return <PreviewModalContent sidebar={<></>} title='Add Offer' />;
|
||||
};
|
||||
|
||||
export default NiceModal.create(AddOfferModal);
|
@ -0,0 +1,22 @@
|
||||
import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
||||
import useFeatureFlag from '../../../../hooks/useFeatureFlag';
|
||||
import useRouting from '../../../../hooks/useRouting';
|
||||
import {PreviewModalContent} from '../../../../admin-x-ds/global/modal/PreviewModal';
|
||||
import {useEffect} from 'react';
|
||||
|
||||
const EditOfferModal = () => {
|
||||
const modal = useModal();
|
||||
const {updateRoute} = useRouting();
|
||||
const hasOffers = useFeatureFlag('adminXOffers');
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasOffers) {
|
||||
modal.remove();
|
||||
updateRoute('');
|
||||
}
|
||||
}, [hasOffers, modal, updateRoute]);
|
||||
|
||||
return <PreviewModalContent sidebar={<></>} title='Edit Offer' />;
|
||||
};
|
||||
|
||||
export default NiceModal.create(EditOfferModal);
|
@ -0,0 +1,22 @@
|
||||
import Modal from '../../../../admin-x-ds/global/modal/Modal';
|
||||
import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
||||
import useFeatureFlag from '../../../../hooks/useFeatureFlag';
|
||||
import useRouting from '../../../../hooks/useRouting';
|
||||
import {useEffect} from 'react';
|
||||
|
||||
const OffersModal = () => {
|
||||
const modal = useModal();
|
||||
const {updateRoute} = useRouting();
|
||||
const hasOffers = useFeatureFlag('adminXOffers');
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasOffers) {
|
||||
modal.remove();
|
||||
updateRoute('');
|
||||
}
|
||||
}, [hasOffers, modal, updateRoute]);
|
||||
|
||||
return <Modal title='Offers' />;
|
||||
};
|
||||
|
||||
export default NiceModal.create(OffersModal);
|
@ -43,7 +43,8 @@ const ALPHA_FEATURES = [
|
||||
'importMemberTier',
|
||||
'lexicalIndicators',
|
||||
'listUnsubscribeHeader',
|
||||
'editorEmojiPicker'
|
||||
'editorEmojiPicker',
|
||||
'adminXOffers'
|
||||
];
|
||||
|
||||
module.exports.GA_KEYS = [...GA_FEATURES];
|
||||
|
Loading…
Reference in New Issue
Block a user