mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
Added latest offer cards to the Offers setting group
refs https://github.com/TryGhost/Product/issues/4176
This commit is contained in:
parent
5587192644
commit
d76b739f1e
2 changed files with 77 additions and 27 deletions
|
@ -1,6 +1,7 @@
|
|||
import React from 'react';
|
||||
import TopLevelGroup from '../../TopLevelGroup';
|
||||
import {Button, withErrorBoundary} from '@tryghost/admin-x-design-system';
|
||||
import {CopyLinkButton} from './offers/OffersIndex';
|
||||
import {checkStripeEnabled} from '@tryghost/admin-x-framework/api/settings';
|
||||
import {useGlobalData} from '../../providers/GlobalDataProvider';
|
||||
import {useRouting} from '@tryghost/admin-x-framework/routing';
|
||||
|
@ -21,7 +22,57 @@ const Offers: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
|||
navid='offers'
|
||||
testId='offers'
|
||||
title='Offers'
|
||||
/>
|
||||
>
|
||||
<div>
|
||||
<div className='grid grid-cols-3 gap-4'>
|
||||
<div className='group flex aspect-square cursor-pointer flex-col justify-between break-words rounded-sm border border-transparent bg-grey-100 p-5 transition-all hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm dark:bg-grey-950 dark:hover:border-grey-800'>
|
||||
<span className='text-[1.65rem] font-bold leading-tight tracking-tight'>Black Friday</span>
|
||||
<div className='flex flex-col'>
|
||||
<span className='text-sm font-semibold uppercase text-green'>20% off</span>
|
||||
<div className='flex gap-1 text-xs'>
|
||||
<span className='font-semibold'>Bronze</span>
|
||||
<span className='text-grey-700'>monthly</span>
|
||||
</div>
|
||||
<div className='mt-2 flex items-end justify-between'>
|
||||
<a className='text-xs text-grey-700 hover:text-black hover:underline' href="#">4 redemptions</a>
|
||||
<CopyLinkButton offerCode='' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='group flex aspect-square cursor-pointer flex-col justify-between break-words rounded-sm border border-transparent bg-grey-100 p-5 transition-all hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm dark:bg-grey-950 dark:hover:border-grey-800'>
|
||||
<span className='text-[1.65rem] font-bold leading-tight tracking-tight'>Cyber Monday</span>
|
||||
<div className='flex flex-col'>
|
||||
<span className='text-sm font-semibold uppercase text-pink'>7 days free</span>
|
||||
<div className='flex gap-1 text-xs'>
|
||||
<span className='font-semibold'>Silver</span>
|
||||
<span className='text-grey-700'>yearly</span>
|
||||
</div>
|
||||
<div className='mt-2 flex items-end justify-between'>
|
||||
<a className='text-xs text-grey-700 hover:text-black hover:underline' href="#">0 redemptions</a>
|
||||
<CopyLinkButton offerCode='' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='group flex aspect-square cursor-pointer flex-col justify-between break-words rounded-sm border border-transparent bg-grey-100 p-5 transition-all hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm dark:bg-grey-950 dark:hover:border-grey-800'>
|
||||
<span className='text-[1.65rem] font-bold leading-tight tracking-tight'>Great Deal</span>
|
||||
<div className='flex flex-col'>
|
||||
<span className='text-sm font-semibold uppercase text-blue'>$20 off</span>
|
||||
<div className='flex gap-1 text-xs'>
|
||||
<span className='font-semibold'>Bronze</span>
|
||||
<span className='text-grey-700'>yearly</span>
|
||||
</div>
|
||||
<div className='mt-2 flex items-end justify-between'>
|
||||
<a className='text-xs text-grey-700 hover:text-black hover:underline' href="#">3 redemptions</a>
|
||||
<CopyLinkButton offerCode='' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mt-4 border-t border-t-grey-200 pt-2'>
|
||||
<Button className='text-sm font-bold text-green' label='Show all' size='sm' link unstyled onClick={openModal} />
|
||||
</div>
|
||||
</div>
|
||||
</TopLevelGroup>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import {Button, Tab, TabView} from '@tryghost/admin-x-design-system';
|
|||
import {Modal} from '@tryghost/admin-x-design-system';
|
||||
import {SortMenu} from '@tryghost/admin-x-design-system';
|
||||
import {Tier, getPaidActiveTiers, useBrowseTiers} from '@tryghost/admin-x-framework/api/tiers';
|
||||
import {Tooltip} from '@tryghost/admin-x-design-system';
|
||||
import {currencyToDecimal, getSymbol} from '../../../../utils/currency';
|
||||
import {getHomepageUrl} from '@tryghost/admin-x-framework/api/site';
|
||||
import {numberWithCommas} from '../../../../utils/helpers';
|
||||
|
@ -64,12 +65,27 @@ const getOfferDiscount = (type: string, amount: number, cadence: string, currenc
|
|||
};
|
||||
};
|
||||
|
||||
const OfferCard: React.FC<{amount: number, cadence: string, currency: string, duration: string, name: string, offerId: string, offerTier: Tier | undefined, redemptionCount: number, type: OfferType, onClick: ()=>void}> = ({amount, cadence, currency, duration, name, offerId, offerTier, redemptionCount, type, onClick}) => {
|
||||
export const CopyLinkButton: React.FC<{offerCode: string}> = ({offerCode}) => {
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const {siteData} = useGlobalData();
|
||||
|
||||
const handleCopyClick = (e?: React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
e?.stopPropagation();
|
||||
const offerLink = `${getHomepageUrl(siteData!)}${offerCode}`;
|
||||
navigator.clipboard.writeText(offerLink);
|
||||
setIsCopied(true);
|
||||
setTimeout(() => setIsCopied(false), 2000);
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
return (
|
||||
<div className='flex cursor-pointer flex-col gap-6 border border-transparent bg-grey-100 p-5 transition-all hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm dark:bg-grey-950 dark:hover:border-grey-800' onClick={onClick}>
|
||||
<div className='group flex cursor-pointer flex-col gap-6 border border-transparent bg-grey-100 p-5 transition-all hover:border-grey-100 hover:bg-grey-75 hover:shadow-sm dark:bg-grey-950 dark:hover:border-grey-800' onClick={onClick}>
|
||||
<div className='flex items-center justify-between'>
|
||||
<h2 className='text-[1.6rem] font-semibold' onClick={onClick}>{name}</h2>
|
||||
<span className={`text-xs font-semibold uppercase ${discountColor}`}>{discountOffer}</span>
|
||||
|
@ -78,35 +94,17 @@ const OfferCard: React.FC<{amount: number, cadence: string, currency: string, du
|
|||
<span className='text-4xl font-bold tracking-tight'>{updatedPriceWithCurrency}</span>
|
||||
<span className='text-[1.6rem] font-medium text-grey-700 line-through'>{originalPriceWithCurrency}</span>
|
||||
</div>
|
||||
<div className='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 className='flex items-end justify-between'>
|
||||
<div className='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>
|
||||
<CopyLinkButton offerCode={code} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const CopyLinkButton: React.FC<{offerCode: string}> = ({offerCode}) => {
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const {siteData} = useGlobalData();
|
||||
|
||||
const handleCopyClick = async () => {
|
||||
const offerLink = `${getHomepageUrl(siteData!)}${offerCode}`;
|
||||
try {
|
||||
await navigator.clipboard.writeText(offerLink);
|
||||
setIsCopied(true);
|
||||
setTimeout(() => setIsCopied(false), 2000); // reset after 2 seconds
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Failed to copy text: ', err);
|
||||
}
|
||||
};
|
||||
|
||||
return isCopied ?
|
||||
<span className='inline-block leading-[22px] text-green'>Copied</span> :
|
||||
<Button className='opacity-0 will-change-[opacity] group-hover:opacity-100' icon='hyperlink-circle' link={true} onClick={handleCopyClick} />;
|
||||
};
|
||||
|
||||
export const OffersIndexModal = () => {
|
||||
const modal = useModal();
|
||||
const {updateRoute} = useRouting();
|
||||
|
@ -167,6 +165,7 @@ export const OffersIndexModal = () => {
|
|||
key={offer?.id}
|
||||
amount={offer?.amount}
|
||||
cadence={offer?.cadence}
|
||||
code={offer?.code}
|
||||
currency={offer?.currency || 'USD'}
|
||||
duration={offer?.duration}
|
||||
name={offer?.name}
|
||||
|
|
Loading…
Reference in a new issue