Update src/components/sections/pricing/PricingCardEight.tsx

This commit is contained in:
2026-01-23 17:23:57 +00:00
parent c2d998832c
commit 21b46532b6

View File

@@ -1,242 +1,98 @@
"use client"; import React from 'react';
import { Check } from 'lucide-react';
import { memo } from "react"; interface PricingFeature {
import CardStack from "@/components/cardStack/CardStack"; text: string;
import Button from "@/components/button/Button"; included: boolean;
import PricingBadge from "@/components/shared/PricingBadge"; }
import PricingFeatureList from "@/components/shared/PricingFeatureList";
import { getButtonProps } from "@/lib/buttonUtils";
import { cls, shouldUseInvertedText } from "@/lib/utils";
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
import type { LucideIcon } from "lucide-react";
import type { ButtonConfig, CardAnimationType, TitleSegment } from "@/components/cardStack/types";
import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants";
type PricingPlan = { interface PricingPlan {
id: string; id: string;
badge: string; name: string;
badgeIcon?: LucideIcon; price: number;
price: string; period: string;
subtitle: string; description: string;
buttons: ButtonConfig[]; features: PricingFeature[];
features: string[]; popular?: boolean;
}; buttonText: string;
}
interface PricingCardEightProps { interface PricingCardEightProps {
plans: PricingPlan[]; plan: PricingPlan;
carouselMode?: "auto" | "buttons"; className?: string;
uniformGridCustomHeightClasses?: string; onSelect?: (planId: string) => void;
animationType: CardAnimationType;
title: string;
titleSegments?: TitleSegment[];
description: string;
tag?: string;
tagIcon?: LucideIcon;
buttons?: ButtonConfig[];
textboxLayout: TextboxLayout;
useInvertedBackground: InvertedBackground;
ariaLabel?: string;
className?: string;
containerClassName?: string;
cardClassName?: string;
textBoxTitleClassName?: string;
textBoxTitleImageWrapperClassName?: string;
textBoxTitleImageClassName?: string;
textBoxDescriptionClassName?: string;
badgeClassName?: string;
priceClassName?: string;
subtitleClassName?: string;
planButtonContainerClassName?: string;
planButtonClassName?: string;
featuresClassName?: string;
featureItemClassName?: string;
gridClassName?: string;
carouselClassName?: string;
controlsClassName?: string;
textBoxClassName?: string;
textBoxTagClassName?: string;
textBoxButtonContainerClassName?: string;
textBoxButtonClassName?: string;
textBoxButtonTextClassName?: string;
} }
interface PricingCardItemProps { export const PricingCardEight: React.FC<PricingCardEightProps> = ({
plan: PricingPlan; plan,
shouldUseLightText: boolean; className = '',
cardClassName?: string; onSelect
badgeClassName?: string; }) => {
priceClassName?: string; const handleSelect = () => {
subtitleClassName?: string; if (onSelect) {
planButtonContainerClassName?: string; onSelect(plan.id);
planButtonClassName?: string; }
featuresClassName?: string; };
featureItemClassName?: string;
}
const PricingCardItem = memo(({ const cardClasses = [
plan, 'relative bg-white rounded-2xl shadow-lg border border-gray-200 p-8',
shouldUseLightText, 'hover:shadow-xl transition-all duration-300',
cardClassName = "", plan.popular ? 'ring-2 ring-blue-500 scale-105' : '',
badgeClassName = "", className
priceClassName = "", ].filter(Boolean).join(' ');
subtitleClassName = "",
planButtonContainerClassName = "",
planButtonClassName = "",
featuresClassName = "",
featureItemClassName = "",
}: PricingCardItemProps) => {
const theme = useTheme();
const getButtonConfigProps = () => { const buttonClasses = [
if (theme.defaultButtonVariant === "hover-bubble") { 'w-full py-3 px-6 rounded-lg font-semibold transition-colors duration-200',
return { bgClassName: "w-full" }; plan.popular
} ? 'bg-blue-600 text-white hover:bg-blue-700'
if (theme.defaultButtonVariant === "icon-arrow") { : 'bg-gray-100 text-gray-900 hover:bg-gray-200 border border-gray-300'
return { className: "justify-between" }; ].join(' ');
}
return {};
};
return ( return (
<div className={cls("relative h-full card text-foreground rounded-theme-capped p-3 flex flex-col gap-3", cardClassName)}> <div className={cardClasses}>
<div className="relative secondary-button p-3 flex flex-col gap-3 rounded-theme-capped" > {plan.popular && (
<PricingBadge <div className="absolute -top-4 left-1/2 transform -translate-x-1/2">
badge={plan.badge} <span className="bg-blue-600 text-white px-4 py-1 rounded-full text-sm font-medium">
badgeIcon={plan.badgeIcon} Most Popular
className={badgeClassName} </span>
/>
<div className="relative z-1 flex flex-col gap-1">
<div className="text-5xl font-medium text-foreground">
{plan.price}
</div>
<p className="text-base text-foreground">
{plan.subtitle}
</p>
</div>
{plan.buttons && plan.buttons.length > 0 && (
<div className={cls("relative z-1 w-full flex flex-col gap-3", planButtonContainerClassName)}>
{plan.buttons.slice(0, 2).map((button, index) => (
<Button
key={`${button.text}-${index}`}
{...getButtonProps(
{ ...button, props: { ...button.props, ...getButtonConfigProps() } },
index,
theme.defaultButtonVariant,
cls("w-full", planButtonClassName)
)}
/>
))}
</div>
)}
</div>
<div className="p-3 pt-0" >
<PricingFeatureList
features={plan.features}
shouldUseLightText={shouldUseLightText}
className={cls("mt-1", featuresClassName)}
featureItemClassName={featureItemClassName}
/>
</div>
</div> </div>
); )}
});
PricingCardItem.displayName = "PricingCardItem"; <div className="text-center">
<h3 className="text-2xl font-bold text-gray-900 mb-2">{plan.name}</h3>
<p className="text-gray-600 mb-6">{plan.description}</p>
<div className="mb-8">
<span className="text-5xl font-bold text-gray-900">${plan.price}</span>
<span className="text-gray-600 ml-2">/{plan.period}</span>
</div>
</div>
const PricingCardEight = ({ <div className="space-y-4 mb-8">
plans, {plan.features.map((feature, index) => (
carouselMode = "buttons", <div key={index} className="flex items-start space-x-3">
uniformGridCustomHeightClasses, <div className={`flex-shrink-0 w-5 h-5 rounded-full flex items-center justify-center mt-0.5 ${
animationType, feature.included
title, ? 'bg-green-100 text-green-600'
titleSegments, : 'bg-gray-100 text-gray-400'
description, }`}>
tag, <Check className="w-3 h-3" />
tagIcon, </div>
buttons, <span className={`text-sm ${
textboxLayout, feature.included ? 'text-gray-900' : 'text-gray-500 line-through'
useInvertedBackground, }`}>
ariaLabel = "Pricing section", {feature.text}
className = "", </span>
containerClassName = "", </div>
cardClassName = "", ))}
textBoxTitleClassName = "", </div>
textBoxTitleImageWrapperClassName = "",
textBoxTitleImageClassName = "",
textBoxDescriptionClassName = "",
badgeClassName = "",
priceClassName = "",
subtitleClassName = "",
planButtonContainerClassName = "",
planButtonClassName = "",
featuresClassName = "",
featureItemClassName = "",
gridClassName = "",
carouselClassName = "",
controlsClassName = "",
textBoxClassName = "",
textBoxTagClassName = "",
textBoxButtonContainerClassName = "",
textBoxButtonClassName = "",
textBoxButtonTextClassName = "",
}: PricingCardEightProps) => {
const theme = useTheme();
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
return ( <button
<CardStack onClick={handleSelect}
useInvertedBackground={useInvertedBackground} className={buttonClasses}
mode={carouselMode} >
gridVariant="uniform-all-items-equal" {plan.buttonText}
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses} </button>
animationType={animationType} </div>
);
title={title} };
titleSegments={titleSegments}
description={description}
tag={tag}
tagIcon={tagIcon}
buttons={buttons}
textboxLayout={textboxLayout}
className={className}
containerClassName={containerClassName}
gridClassName={gridClassName}
carouselClassName={carouselClassName}
controlsClassName={controlsClassName}
textBoxClassName={textBoxClassName}
titleClassName={textBoxTitleClassName}
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
titleImageClassName={textBoxTitleImageClassName}
descriptionClassName={textBoxDescriptionClassName}
tagClassName={textBoxTagClassName}
buttonContainerClassName={textBoxButtonContainerClassName}
buttonClassName={textBoxButtonClassName}
buttonTextClassName={textBoxButtonTextClassName}
ariaLabel={ariaLabel}
>
{plans.map((plan, index) => (
<PricingCardItem
key={`${plan.id}-${index}`}
plan={plan}
shouldUseLightText={shouldUseLightText}
cardClassName={cardClassName}
badgeClassName={badgeClassName}
priceClassName={priceClassName}
subtitleClassName={subtitleClassName}
planButtonContainerClassName={planButtonContainerClassName}
planButtonClassName={planButtonClassName}
featuresClassName={featuresClassName}
featureItemClassName={featureItemClassName}
/>
))}
</CardStack>
);
};
PricingCardEight.displayName = "PricingCardEight";
export default PricingCardEight;