mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-08 02:52:39 -05:00
Fixed duplicate modals issue in offers AdminX
refs https://github.com/TryGhost/Product/issues/4138 - we should follow the same pattern as change modal flow for the Offers modals - with the change theme flow, only one modal is shown at a time to make it feel like navigating inside one container modal - this removes the container modal from OffersContainerModal and puts back the Modal component to each screens, so there will be no modal on top of another
This commit is contained in:
parent
9016d76d42
commit
6de3e85bd0
6 changed files with 98 additions and 75 deletions
|
@ -31,10 +31,9 @@ export const modalPaths: {[key: string]: ModalName} = {
|
|||
'recommendations/edit': 'EditRecommendationModal',
|
||||
'announcement-bar/edit': 'AnnouncementBarModal',
|
||||
'embed-signup-form/show': 'EmbedSignupFormModal',
|
||||
'offers/edit/*': 'OffersContainerModal',
|
||||
// 'offers/new': 'AddOfferModal',
|
||||
// 'offers/success/:id': 'OfferSuccess',
|
||||
// 'offers/:id': 'EditOfferModal',
|
||||
'offers/edit': 'OffersContainerModal',
|
||||
'offers/edit/:id': 'OffersContainerModal',
|
||||
'offers/new': 'OffersContainerModal',
|
||||
about: 'AboutModal'
|
||||
};
|
||||
|
||||
|
|
|
@ -429,11 +429,12 @@ const AddOfferModal = () => {
|
|||
cancelLabel='Cancel'
|
||||
deviceSelector={false}
|
||||
dirty={saveState === 'unsaved'}
|
||||
height='full'
|
||||
okColor={okProps.color}
|
||||
okLabel='Publish'
|
||||
preview={iframe}
|
||||
sidebar={sidebar}
|
||||
size='full'
|
||||
size='lg'
|
||||
title='Offer'
|
||||
onCancel={cancelAddOffer}
|
||||
onOk={async () => {
|
||||
|
|
|
@ -253,13 +253,15 @@ const EditOfferModal: React.FC<{id: string}> = ({id}) => {
|
|||
href={href}
|
||||
/>;
|
||||
|
||||
return offerById ? <PreviewModalContent deviceSelector={false}
|
||||
return offerById ? <PreviewModalContent
|
||||
deviceSelector={false}
|
||||
dirty={saveState === 'unsaved'}
|
||||
height='full'
|
||||
okColor={okProps.color}
|
||||
okLabel={okProps.label || 'Save'}
|
||||
preview={iframe}
|
||||
sidebar={sidebar}
|
||||
size='full'
|
||||
size='lg'
|
||||
testId='offer-update-modal'
|
||||
title='Offer'
|
||||
onCancel={() => {
|
||||
|
|
|
@ -1,22 +1,27 @@
|
|||
import {Button} from '@tryghost/admin-x-design-system';
|
||||
import {Icon} from '@tryghost/admin-x-design-system';
|
||||
import {Modal} from '@tryghost/admin-x-design-system';
|
||||
import {Offer, useBrowseOffersById} from '@tryghost/admin-x-framework/api/offers';
|
||||
import {getHomepageUrl} from '@tryghost/admin-x-framework/api/site';
|
||||
import {useBrowseOffersById} from '@tryghost/admin-x-framework/api/offers';
|
||||
import {useEffect, useState} from 'react';
|
||||
import {useGlobalData} from '../../../providers/GlobalDataProvider';
|
||||
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||
|
||||
const OfferSuccess: React.FC<{id: string}> = ({id}) => {
|
||||
const {updateRoute} = useRouting();
|
||||
const {data: {offers: offerById = []} = {}} = useBrowseOffersById(id ? id : '');
|
||||
|
||||
const [offer, setOffer] = useState<Offer>();
|
||||
const [offerLink, setOfferLink] = useState<string>('');
|
||||
|
||||
const {siteData} = useGlobalData();
|
||||
|
||||
useEffect(() => {
|
||||
if (offerById.length > 0) {
|
||||
const offer = offerById[0];
|
||||
const offerUrl = `${getHomepageUrl(siteData!)}${offer?.code}`;
|
||||
const currentOffer = offerById[0];
|
||||
const offerUrl = `${getHomepageUrl(siteData!)}${currentOffer?.code}`;
|
||||
setOfferLink(offerUrl);
|
||||
setOffer(currentOffer);
|
||||
}
|
||||
}, [offerById, siteData]);
|
||||
|
||||
|
@ -33,20 +38,43 @@ const OfferSuccess: React.FC<{id: string}> = ({id}) => {
|
|||
}
|
||||
};
|
||||
|
||||
return <div className='-mt-6 flex h-full flex-col items-center justify-center text-center'>
|
||||
<Icon name='tags-check' size='xl' />
|
||||
<h1 className='mt-6 text-4xl'>Your new offer is live!</h1>
|
||||
<p className='mt-3 max-w-[510px] text-[1.6rem]'>You can share the link anywhere. In your newsletter, social media, a podcast, or in-person. It all just works.</p>
|
||||
<div className='mt-8 flex w-full max-w-md flex-col gap-8'>
|
||||
<Button color='green' label={isCopied ? 'Copied!' : 'Copy link'} fullWidth onClick={handleCopyClick} />
|
||||
<div className='flex items-center gap-4 text-xs font-medium before:h-px before:grow before:bg-grey-300 before:content-[""] after:h-px after:grow after:bg-grey-300 after:content-[""]'>OR</div>
|
||||
<div className='flex gap-2'>
|
||||
<Button className='h-8 border border-grey-300' disabled={true} icon='twitter-x' iconColorClass='w-[14px] h-[14px]' size='sm' fullWidth />
|
||||
<Button className='h-8 border border-grey-300' disabled={true} icon='facebook' size='sm' fullWidth />
|
||||
<Button className='h-8 border border-grey-300' disabled={true} icon='linkedin' size='sm' fullWidth />
|
||||
const handleTwitter = () => {
|
||||
window.open(`https://twitter.com/intent/tweet?url=${encodeURI(offerLink)}&text=${encodeURIComponent(offer?.name || '')}`, '_blank');
|
||||
};
|
||||
|
||||
const handleFacebook = () => {
|
||||
window.open(`https://www.facebook.com/sharer/sharer.php?u=${encodeURI(offerLink)}`, '_blank');
|
||||
};
|
||||
|
||||
const handleLinkedIn = () => {
|
||||
window.open(`http://www.linkedin.com/shareArticle?mini=true&url=${encodeURI(offerLink)}&title=${encodeURIComponent(offer?.name || '')}`, '_blank');
|
||||
};
|
||||
|
||||
return <Modal
|
||||
afterClose={() => {
|
||||
updateRoute('offers');
|
||||
}}
|
||||
animate={false}
|
||||
footer={false}
|
||||
height='full'
|
||||
size='lg'
|
||||
topRightContent='close'
|
||||
>
|
||||
<div className='-mt-6 flex h-full flex-col items-center justify-center text-center'>
|
||||
<Icon name='tags-check' size='xl' />
|
||||
<h1 className='mt-6 text-4xl'>Your new offer is live!</h1>
|
||||
<p className='mt-3 max-w-[510px] text-[1.6rem]'>You can share the link anywhere. In your newsletter, social media, a podcast, or in-person. It all just works.</p>
|
||||
<div className='mt-8 flex w-full max-w-md flex-col gap-8'>
|
||||
<Button color='green' label={isCopied ? 'Copied!' : 'Copy link'} fullWidth onClick={handleCopyClick} />
|
||||
<div className='flex items-center gap-4 text-xs font-medium before:h-px before:grow before:bg-grey-300 before:content-[""] after:h-px after:grow after:bg-grey-300 after:content-[""]'>OR</div>
|
||||
<div className='flex gap-2'>
|
||||
<Button className='h-8 border border-grey-300' icon='twitter-x' iconColorClass='w-[14px] h-[14px]' size='sm' fullWidth onClick={handleTwitter} />
|
||||
<Button className='h-8 border border-grey-300' icon='facebook' size='sm' fullWidth onClick={handleFacebook} />
|
||||
<Button className='h-8 border border-grey-300' icon='linkedin' size='sm' fullWidth onClick={handleLinkedIn} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
</Modal>;
|
||||
};
|
||||
|
||||
export default OfferSuccess;
|
||||
|
|
|
@ -2,60 +2,30 @@ import AddOfferModal from './AddOfferModal';
|
|||
import EditOfferModal from './EditOfferModal';
|
||||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import OfferSuccess from './OfferSuccess';
|
||||
import {Button, Modal} from '@tryghost/admin-x-design-system';
|
||||
import {OffersIndexModal} from './OffersIndex';
|
||||
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||
import {useState} from 'react';
|
||||
|
||||
type OffersRouteHandlerProps = {
|
||||
route: string;
|
||||
setIsIndex: (value: boolean) => void;
|
||||
};
|
||||
|
||||
const OffersRouteHandler: React.FC<OffersRouteHandlerProps> = ({route, setIsIndex}) => {
|
||||
const OffersRouteHandler: React.FC<OffersRouteHandlerProps> = ({route}) => {
|
||||
if (route === 'offers/new') {
|
||||
setIsIndex(false);
|
||||
return <AddOfferModal />;
|
||||
} else if (route.startsWith('offers/edit/') && route.length > 'offers/edit/'.length) {
|
||||
setIsIndex(false);
|
||||
const offerId = route.split('/').pop();
|
||||
return <EditOfferModal id={offerId ? offerId : ''} />;
|
||||
} else if (route.startsWith('offers/success/') && route.length > 'offers/success/'.length) {
|
||||
setIsIndex(false);
|
||||
const offerId = route.split('/').pop();
|
||||
return <OfferSuccess id={offerId ? offerId : ''} />;
|
||||
} else {
|
||||
setIsIndex(true);
|
||||
return <OffersIndexModal />;
|
||||
}
|
||||
};
|
||||
|
||||
const OffersContainerModal = () => {
|
||||
const {route, updateRoute} = useRouting();
|
||||
const [isIndex, setIsIndex] = useState<boolean>(true);
|
||||
return (
|
||||
<Modal
|
||||
afterClose={() => {
|
||||
updateRoute('offers');
|
||||
}}
|
||||
cancelLabel=''
|
||||
footer={
|
||||
isIndex && <div className='mx-8 flex w-full items-center justify-between'>
|
||||
<a className='text-sm' href="https://ghost.org/help/offers" rel="noopener noreferrer" target="_blank">→ Learn about offers in Ghost</a>
|
||||
<Button color='black' label='Close' onClick={() => {
|
||||
updateRoute('offers');
|
||||
}} />
|
||||
</div>
|
||||
}
|
||||
header={false}
|
||||
height='full'
|
||||
size='lg'
|
||||
stickyFooter= {isIndex}
|
||||
testId='offers-modal'
|
||||
>
|
||||
<OffersRouteHandler route={route} setIsIndex={setIsIndex} />
|
||||
</Modal>
|
||||
);
|
||||
const {route} = useRouting();
|
||||
return <OffersRouteHandler route={route} />;
|
||||
};
|
||||
|
||||
export default NiceModal.create(OffersContainerModal);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import useFeatureFlag from '../../../../hooks/useFeatureFlag';
|
||||
import {Button, Tab, TabView} from '@tryghost/admin-x-design-system';
|
||||
import {Modal} from '@tryghost/admin-x-design-system';
|
||||
import {Tier, getPaidActiveTiers, useBrowseTiers} from '@tryghost/admin-x-framework/api/tiers';
|
||||
import {currencyToDecimal, getSymbol} from '../../../../utils/currency';
|
||||
import {getHomepageUrl} from '@tryghost/admin-x-framework/api/site';
|
||||
|
@ -193,26 +194,48 @@ export const OffersIndexModal = () => {
|
|||
})}
|
||||
</table>;
|
||||
|
||||
return <div className='pt-6'>
|
||||
<header>
|
||||
<div className='flex items-center justify-between'>
|
||||
<TabView
|
||||
border={false}
|
||||
selectedTab={selectedTab}
|
||||
tabs={offersTabs}
|
||||
width='wide'
|
||||
onTabChange={setSelectedTab}
|
||||
/>
|
||||
<Button color='green' icon='add' iconColorClass='green' label='New offer' link={true} size='sm' onClick={() => updateRoute('offers/new')} />
|
||||
return <Modal
|
||||
afterClose={() => {
|
||||
updateRoute('offers');
|
||||
}}
|
||||
animate={false}
|
||||
cancelLabel=''
|
||||
footer={
|
||||
<div className='mx-8 flex w-full items-center justify-between'>
|
||||
<a className='text-sm' href="https://ghost.org/help/offers" rel="noopener noreferrer" target="_blank">→ Learn about offers in Ghost</a>
|
||||
<Button color='black' label='Close' onClick={() => {
|
||||
modal.remove();
|
||||
updateRoute('offers');
|
||||
}} />
|
||||
</div>
|
||||
<div className='mt-12 flex items-center justify-between border-b border-b-grey-300 pb-2.5'>
|
||||
<h1 className='text-3xl'>{offersTabs.find(tab => tab.id === selectedTab)?.title} offers</h1>
|
||||
<div className='flex gap-3'>
|
||||
<Button icon='layout-module-1' iconColorClass={selectedLayout === 'card' ? 'text-black' : 'text-grey-500'} link={true} size='sm' onClick={() => setSelectedLayout('card')} />
|
||||
<Button icon='layout-headline' iconColorClass={selectedLayout === 'list' ? 'text-black' : 'text-grey-500'} link={true} size='sm' onClick={() => setSelectedLayout('list')} />
|
||||
}
|
||||
header={false}
|
||||
height='full'
|
||||
size='lg'
|
||||
testId='offers-modal'
|
||||
stickyFooter
|
||||
>
|
||||
<div className='pt-6'>
|
||||
<header>
|
||||
<div className='flex items-center justify-between'>
|
||||
<TabView
|
||||
border={false}
|
||||
selectedTab={selectedTab}
|
||||
tabs={offersTabs}
|
||||
width='wide'
|
||||
onTabChange={setSelectedTab}
|
||||
/>
|
||||
<Button color='green' icon='add' iconColorClass='green' label='New offer' link={true} size='sm' onClick={() => updateRoute('offers/new')} />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{selectedLayout === 'card' ? cardLayoutOutput : listLayoutOutput}
|
||||
</div>;
|
||||
<div className='mt-12 flex items-center justify-between border-b border-b-grey-300 pb-2.5'>
|
||||
<h1 className='text-3xl'>{offersTabs.find(tab => tab.id === selectedTab)?.title} offers</h1>
|
||||
<div className='flex gap-3'>
|
||||
<Button icon='layout-module-1' iconColorClass={selectedLayout === 'card' ? 'text-black' : 'text-grey-500'} link={true} size='sm' onClick={() => setSelectedLayout('card')} />
|
||||
<Button icon='layout-headline' iconColorClass={selectedLayout === 'list' ? 'text-black' : 'text-grey-500'} link={true} size='sm' onClick={() => setSelectedLayout('list')} />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{selectedLayout === 'card' ? cardLayoutOutput : listLayoutOutput}
|
||||
</div>
|
||||
</Modal>;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue