mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Simplified offers list screen in AdminX (#19190)
fixes ADM-40 - removed layout switch feature, and made the list layout default - removed the help link, because it will no longer be necessary on the list screen - fixed offers count in the table header
This commit is contained in:
parent
49eaee40f2
commit
63774463ce
1 changed files with 7 additions and 85 deletions
|
@ -89,42 +89,6 @@ export const CopyLinkButton: React.FC<{offerCode: string}> = ({offerCode}) => {
|
|||
return <Tooltip containerClassName='group-hover:opacity-100 opacity-0 flex items-center -mr-1 justify-center leading-none w-5 h-5' content={isCopied ? 'Copied' : 'Copy link'} size='sm'><Button color='clear' hideLabel={true} icon={isCopied ? 'check-circle' : 'hyperlink-circle'} iconColorClass={isCopied ? 'text-green w-[14px] h-[14px]' : 'w-[18px] h-[18px]'} label={isCopied ? 'Copied' : 'Copy'} unstyled={true} onClick={handleCopyClick} /></Tooltip>;
|
||||
};
|
||||
|
||||
const OfferCard: React.FC<{amount: number, cadence: string, currency: string, duration: string, name: string, code: string, offerId: string, offerTier: Tier | undefined, redemptionCount: number, type: OfferType, onClick: ()=>void}> = ({amount, cadence, currency, duration, name, code, offerId, offerTier, redemptionCount, type, onClick}) => {
|
||||
let tierName = offerTier?.name + ' ' + getOfferCadence(cadence) + ' — ' + getOfferDuration(duration);
|
||||
const {discountColor, discountOffer, originalPriceWithCurrency, updatedPriceWithCurrency} = getOfferDiscount(type, amount, cadence, currency || 'USD', offerTier);
|
||||
|
||||
const isTierArchived = offerTier?.active === false;
|
||||
|
||||
return (
|
||||
<div className={`group relative ${isTierArchived ? '' : 'cursor-pointer'} border border-transparent bg-grey-100 p-5 transition-all ${!isTierArchived ? 'hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm' : ''} dark:bg-grey-950 ${!isTierArchived ? 'dark:hover:border-grey-800' : ''}`} onClick={!isTierArchived ? onClick : () => {}}>
|
||||
<div className='flex h-full flex-col gap-6'>
|
||||
<div className={`${isTierArchived ? 'pointer-events-none opacity-50' : ''} flex grow items-baseline justify-between gap-2`}>
|
||||
<h2 className='text-[1.6rem] font-semibold'>{name}</h2>
|
||||
<span className={`-translate-y-px whitespace-nowrap text-xs font-semibold uppercase ${discountColor}`}>{discountOffer}</span>
|
||||
</div>
|
||||
<div className={`${isTierArchived ? 'pointer-events-none opacity-50' : ''} flex items-baseline gap-1`}>
|
||||
<span className='text-3xl font-bold tracking-tight'>{updatedPriceWithCurrency}</span>
|
||||
<span className='text-[1.6rem] font-medium text-grey-700 line-through'>{originalPriceWithCurrency}</span>
|
||||
</div>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className={`${isTierArchived ? 'pointer-events-none opacity-50' : ''} flex flex-col items-start text-xs`}>
|
||||
<span className='font-medium'>{tierName}</span>
|
||||
<a className='text-grey-700 hover:underline' href={createRedemptionFilterUrl(offerId)}>{redemptionCount} redemptions</a>
|
||||
</div>
|
||||
{!isTierArchived ?
|
||||
<CopyLinkButton offerCode={code} /> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{isTierArchived ?
|
||||
<div className='absolute left-4 top-4 whitespace-nowrap rounded-sm bg-black px-2 py-0.5 text-xs leading-normal text-white opacity-0 transition-all group-hover:opacity-100 dark:bg-grey-950'>This offer is disabled, because <br /> it is tied to an archived tier.</div> :
|
||||
null
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const OffersIndexModal = () => {
|
||||
const modal = useModal();
|
||||
const {updateRoute} = useRouting();
|
||||
|
@ -149,7 +113,6 @@ export const OffersIndexModal = () => {
|
|||
{id: 'archived', title: 'Archived'}
|
||||
];
|
||||
const [selectedTab, setSelectedTab] = useState('active');
|
||||
const [selectedLayout, setSelectedLayout] = useState('card');
|
||||
const [sortOption, setSortOption] = useState('date-added');
|
||||
const [sortDirection, setSortDirection] = useState('desc');
|
||||
|
||||
|
@ -179,43 +142,11 @@ export const OffersIndexModal = () => {
|
|||
}
|
||||
});
|
||||
|
||||
const cardLayoutOutput = <div className='mt-8 grid grid-cols-1 gap-6 min-[600px]:grid-cols-2 min-[900px]:grid-cols-3'>
|
||||
{sortedOffers.filter((offer) => {
|
||||
const offerTier = allTiers?.find(tier => tier.id === offer?.tier.id);
|
||||
//Check to filter out offers with archived offerTier
|
||||
return (selectedTab === 'active' && (offer.status === 'active' && offerTier && offerTier.active === true)) ||
|
||||
(selectedTab === 'archived' && (offer.status === 'archived' || (offerTier && offerTier.active === false)));
|
||||
}).map((offer) => {
|
||||
const offerTier = allTiers?.find(tier => tier.id === offer?.tier.id);
|
||||
|
||||
if (!offerTier) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<OfferCard
|
||||
key={offer?.id}
|
||||
amount={offer?.amount}
|
||||
cadence={offer?.cadence}
|
||||
code={offer?.code}
|
||||
currency={offer?.currency || 'USD'}
|
||||
duration={offer?.duration}
|
||||
name={offer?.name}
|
||||
offerId={offer?.id ? offer.id : ''}
|
||||
offerTier={offerTier}
|
||||
redemptionCount={offer?.redemption_count ? offer.redemption_count : 0}
|
||||
type={offer?.type as OfferType}
|
||||
onClick={() => handleOfferEdit(offer?.id ? offer.id : '')}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>;
|
||||
|
||||
const listLayoutOutput = <div className='overflow-x-auto'>
|
||||
<table className='m-0 w-full'>
|
||||
{(selectedTab === 'active' && activeOffers.length > 0) || (selectedTab === 'archived' && archivedOffers.length > 0) ?
|
||||
<tr className='border-b border-b-grey-300'>
|
||||
<th className='px-5 py-2.5 pl-0 text-xs font-normal text-grey-700'>{sortedOffers.length} {sortedOffers.length > 1 ? 'offers' : 'offer'}</th>
|
||||
<th className='px-5 py-2.5 pl-0 text-xs font-normal text-grey-700'>{selectedTab === 'active' ? activeOffers.length : archivedOffers.length} {selectedTab === 'active' ? (activeOffers.length !== 1 ? 'offers' : 'offer') : (archivedOffers.length !== 1 ? 'offers' : 'offer')}</th>
|
||||
<th className='px-5 py-2.5 text-xs font-normal text-grey-700'>Tier</th>
|
||||
<th className='px-5 py-2.5 text-xs font-normal text-grey-700'>Terms</th>
|
||||
<th className='px-5 py-2.5 text-xs font-normal text-grey-700'>Price</th>
|
||||
|
@ -264,20 +195,15 @@ export const OffersIndexModal = () => {
|
|||
}}
|
||||
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>
|
||||
}
|
||||
header={false}
|
||||
height='full'
|
||||
size='lg'
|
||||
testId='offers-modal'
|
||||
stickyFooter
|
||||
onOk={() => {
|
||||
modal.remove();
|
||||
updateRoute('offers');
|
||||
}}
|
||||
>
|
||||
<div className='pt-6'>
|
||||
<header>
|
||||
|
@ -295,7 +221,7 @@ export const OffersIndexModal = () => {
|
|||
</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'>
|
||||
<div className='-mr-3'>
|
||||
<SortMenu
|
||||
direction='desc'
|
||||
items={[
|
||||
|
@ -312,10 +238,6 @@ export const OffersIndexModal = () => {
|
|||
setSortOption(selectedOption);
|
||||
}}
|
||||
/>
|
||||
<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>
|
||||
</div>
|
||||
</header>
|
||||
|
@ -331,7 +253,7 @@ export const OffersIndexModal = () => {
|
|||
</NoValueLabel> :
|
||||
null
|
||||
}
|
||||
{selectedLayout === 'card' ? cardLayoutOutput : listLayoutOutput}
|
||||
{listLayoutOutput}
|
||||
</div>
|
||||
</Modal>;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue