mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-29 18:13:47 +00:00
refactor: extract purchase/renewal flow to separate page
Move ~1900 lines of purchase/renewal logic from Subscription.tsx into a new SubscriptionPurchase.tsx page at /subscription/purchase. - Create SubscriptionPurchase.tsx with tariffs grid, classic mode wizard, switch preview modal, and promo handling - Create PurchaseCTAButton.tsx with animated gradient border, state-aware colors and context-aware text - Create subscriptionHelpers.ts with shared utilities - Add CopyIcon to shared icons module - Register /subscription/purchase route with lazy loading - Update SubscriptionCardExpired and PromoOffersSection navigators - Add CTA translation keys to ru.json and en.json - Remove all scrollToExtend references
This commit is contained in:
@@ -156,7 +156,7 @@ export default function PromoOffersSection({ className = '' }: PromoOffersSectio
|
||||
};
|
||||
|
||||
const handleUseNow = () => {
|
||||
navigate('/subscription', { state: { scrollToExtend: true } });
|
||||
navigate('/subscription/purchase');
|
||||
};
|
||||
|
||||
const handleDeactivateClick = () => {
|
||||
|
||||
@@ -139,8 +139,7 @@ export default function SubscriptionCardExpired({ subscription }: SubscriptionCa
|
||||
{/* Action buttons */}
|
||||
<div className="flex gap-2.5">
|
||||
<Link
|
||||
to="/subscription"
|
||||
state={{ scrollToExtend: true }}
|
||||
to="/subscription/purchase"
|
||||
className="flex flex-1 items-center justify-center rounded-[14px] py-3.5 text-[15px] font-semibold tracking-tight text-white transition-all duration-300"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #FF3B5C, #FF6B35)',
|
||||
|
||||
@@ -242,6 +242,22 @@ export const CheckIcon = ({ className }: IconProps) => (
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const CopyIcon = ({ className }: IconProps) => (
|
||||
<svg
|
||||
className={cn('h-4 w-4', className)}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={1.5}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const XIcon = ({ className }: IconProps) => (
|
||||
<svg
|
||||
className={cn('h-4 w-4', className)}
|
||||
|
||||
93
src/components/subscription/PurchaseCTAButton.tsx
Normal file
93
src/components/subscription/PurchaseCTAButton.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import { Link } from 'react-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { HoverBorderGradient } from '../ui/hover-border-gradient';
|
||||
import type { Subscription } from '../../types';
|
||||
|
||||
interface PurchaseCTAButtonProps {
|
||||
subscription: Subscription | null;
|
||||
}
|
||||
|
||||
export default function PurchaseCTAButton({ subscription }: PurchaseCTAButtonProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isExpired = !subscription || (!subscription.is_active && !subscription.is_trial);
|
||||
const isTrial = subscription?.is_trial;
|
||||
|
||||
const accentColor = isExpired ? '#FF3B5C' : '#3EDBB0';
|
||||
|
||||
const buttonText = isExpired
|
||||
? t('subscription.getSubscription')
|
||||
: isTrial
|
||||
? t('subscription.trialUpgrade.title')
|
||||
: t('subscription.extend');
|
||||
|
||||
const hintText = isExpired
|
||||
? t('subscription.cta.expiredHint')
|
||||
: isTrial
|
||||
? t('subscription.cta.trialHint')
|
||||
: t('subscription.cta.activeHint');
|
||||
|
||||
return (
|
||||
<Link to="/subscription/purchase" className="block">
|
||||
<HoverBorderGradient
|
||||
accentColor={accentColor}
|
||||
duration={4}
|
||||
className="group relative w-full cursor-pointer overflow-hidden rounded-2xl p-[1.5px]"
|
||||
>
|
||||
<div
|
||||
className="relative flex items-center justify-between rounded-[14px] px-5 py-4 transition-all duration-300"
|
||||
style={{
|
||||
background: isExpired
|
||||
? 'linear-gradient(135deg, rgba(255,59,92,0.08), rgba(255,107,53,0.06))'
|
||||
: 'linear-gradient(135deg, rgba(62,219,176,0.08), rgba(0,229,160,0.06))',
|
||||
}}
|
||||
>
|
||||
{/* Left: icon + text */}
|
||||
<div className="flex items-center gap-3">
|
||||
{/* Sparkle icon */}
|
||||
<div
|
||||
className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl"
|
||||
style={{
|
||||
background: isExpired ? 'rgba(255,59,92,0.12)' : 'rgba(62,219,176,0.12)',
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke={accentColor}
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[15px] font-semibold text-dark-50">{buttonText}</div>
|
||||
<div className="text-[12px] text-dark-50/40">{hintText}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right: chevron */}
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
className="flex-shrink-0 text-dark-50/30 transition-transform duration-300 group-hover:translate-x-1"
|
||||
>
|
||||
<path d="M9 18l6-6-6-6" />
|
||||
</svg>
|
||||
</div>
|
||||
</HoverBorderGradient>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user