207 lines
7.1 KiB
TypeScript
207 lines
7.1 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import { useTranslations } from "next-intl";
|
|
import clsx from "clsx";
|
|
|
|
import { Button, Card, Spinner, Typography } from "@/components/ui";
|
|
import { getFormattedPrice } from "@/shared/utils/price";
|
|
import { Currency } from "@/types";
|
|
|
|
import styles from "./VideoGuideCard.module.scss";
|
|
|
|
interface VideoGuideCardProps {
|
|
name: string;
|
|
description: string;
|
|
imageUrl: string;
|
|
duration: string;
|
|
price: number;
|
|
oldPrice: number;
|
|
discount: number;
|
|
isPurchased: boolean;
|
|
isCheckoutLoading?: boolean;
|
|
isProcessingPurchase?: boolean;
|
|
onPurchaseClick?: () => void;
|
|
className?: string;
|
|
}
|
|
|
|
export default function VideoGuideCard(props: VideoGuideCardProps) {
|
|
const {
|
|
name,
|
|
description,
|
|
imageUrl,
|
|
duration,
|
|
price,
|
|
oldPrice,
|
|
discount,
|
|
isPurchased,
|
|
isCheckoutLoading,
|
|
isProcessingPurchase,
|
|
onPurchaseClick,
|
|
className,
|
|
} = props;
|
|
|
|
const tCommon = useTranslations("Dashboard.videoGuides");
|
|
|
|
const currency = Currency.USD;
|
|
|
|
// Если идет обработка покупки - показываем только лоадер на всей карточке
|
|
if (isProcessingPurchase) {
|
|
return (
|
|
<Card className={clsx(styles.container, className, styles.processing)}>
|
|
<div className={styles.processingOverlay}>
|
|
<Spinner size={40} />
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Card
|
|
className={clsx(
|
|
styles.container,
|
|
className,
|
|
isPurchased && styles.purchased
|
|
)}
|
|
>
|
|
{/* Image with Play Icon */}
|
|
<div className={styles.image}>
|
|
<Image
|
|
src={imageUrl}
|
|
alt={name}
|
|
width={260}
|
|
height={160}
|
|
unoptimized
|
|
className={styles.imageContent}
|
|
/>
|
|
<div className={styles.playIcon}>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="64"
|
|
height="65"
|
|
viewBox="0 0 64 65"
|
|
fill="none"
|
|
>
|
|
<g filter="url(#filter0_d_2540_2312)">
|
|
<path
|
|
d="M48.25 31C48.25 26.7247 46.538 22.6246 43.4905 19.6015C40.443 16.5784 36.3098 14.88 32 14.88C27.6902 14.88 23.557 16.5784 20.5095 19.6015C17.462 22.6246 15.75 26.7247 15.75 31C15.75 35.2753 17.462 39.3755 20.5095 42.3986C23.557 45.4217 27.6902 47.12 32 47.12C36.3098 47.12 40.443 45.4217 43.4905 42.3986C46.538 39.3755 48.25 35.2753 48.25 31ZM12 31C12 25.7381 14.1071 20.6918 17.8579 16.971C21.6086 13.2503 26.6957 11.16 32 11.16C37.3043 11.16 42.3914 13.2503 46.1421 16.971C49.8929 20.6918 52 25.7381 52 31C52 36.2619 49.8929 41.3083 46.1421 45.029C42.3914 48.7498 37.3043 50.84 32 50.84C26.6957 50.84 21.6086 48.7498 17.8579 45.029C14.1071 41.3083 12 36.2619 12 31ZM26.7109 22.5603C27.3047 22.2348 28.0234 22.2425 28.6094 22.599L39.8594 29.419C40.4141 29.76 40.7578 30.3568 40.7578 31.0078C40.7578 31.6588 40.4141 32.2555 39.8594 32.5965L28.6094 39.4165C28.0313 39.7653 27.3047 39.7808 26.7109 39.4553C26.1172 39.1298 25.75 38.5098 25.75 37.8355V24.18C25.75 23.5058 26.1172 22.8858 26.7109 22.5603Z"
|
|
fill="white"
|
|
/>
|
|
</g>
|
|
<defs>
|
|
<filter
|
|
id="filter0_d_2540_2312"
|
|
x="-8.75"
|
|
y="-10"
|
|
width="84"
|
|
height="86"
|
|
filterUnits="userSpaceOnUse"
|
|
colorInterpolationFilters="sRGB"
|
|
>
|
|
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
|
<feColorMatrix
|
|
in="SourceAlpha"
|
|
type="matrix"
|
|
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
|
result="hardAlpha"
|
|
/>
|
|
<feOffset dy="2" />
|
|
<feGaussianBlur stdDeviation="6" />
|
|
<feComposite in2="hardAlpha" operator="out" />
|
|
<feColorMatrix
|
|
type="matrix"
|
|
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
|
|
/>
|
|
<feBlend
|
|
mode="normal"
|
|
in2="BackgroundImageFix"
|
|
result="effect1_dropShadow_2540_2312"
|
|
/>
|
|
<feBlend
|
|
mode="normal"
|
|
in="SourceGraphic"
|
|
in2="effect1_dropShadow_2540_2312"
|
|
result="shape"
|
|
/>
|
|
</filter>
|
|
</defs>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className={styles.content}>
|
|
{/* Top Section */}
|
|
<div className={styles.top}>
|
|
<div className={styles.text}>
|
|
<Typography as="h4" className={styles.title} align="left">
|
|
{name}
|
|
</Typography>
|
|
<Typography as="p" className={styles.subtitle} align="left">
|
|
{description}
|
|
</Typography>
|
|
</div>
|
|
{isPurchased && (
|
|
<button className={styles.arrowButton}>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
width="8"
|
|
height="14"
|
|
viewBox="0 0 8 14"
|
|
fill="none"
|
|
>
|
|
<path
|
|
d="M7.70859 6.29609C8.09922 6.68672 8.09922 7.32109 7.70859 7.71172L1.70859 13.7117C1.31797 14.1023 0.683594 14.1023 0.292969 13.7117C-0.0976562 13.3211 -0.0976562 12.6867 0.292969 12.2961L5.58672 7.00234L0.296094 1.70859C-0.0945313 1.31797 -0.0945313 0.683594 0.296094 0.292969C0.686719 -0.0976562 1.32109 -0.0976562 1.71172 0.292969L7.71172 6.29297L7.70859 6.29609Z"
|
|
fill="#A0A7B5"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Bottom Section */}
|
|
<div className={styles.bottom}>
|
|
{!isPurchased ? (
|
|
<>
|
|
<div className={styles.bottomText}>
|
|
<Typography className={styles.duration} align="left">
|
|
{duration}
|
|
</Typography>
|
|
<div className={styles.discountBadge}>
|
|
<Typography className={styles.discountText}>
|
|
{discount}% OFF{" "}
|
|
<span className={styles.oldPrice}>
|
|
{getFormattedPrice(oldPrice, currency)}
|
|
</span>
|
|
</Typography>
|
|
</div>
|
|
</div>
|
|
<Button
|
|
className={styles.buyButton}
|
|
onClick={e => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
onPurchaseClick?.();
|
|
}}
|
|
disabled={isCheckoutLoading}
|
|
>
|
|
{isCheckoutLoading ? (
|
|
<Spinner size={20} />
|
|
) : (
|
|
tCommon("purchaseFor", {
|
|
price: getFormattedPrice(price, currency),
|
|
})
|
|
)}
|
|
</Button>
|
|
</>
|
|
) : (
|
|
<Typography className={styles.durationPurchased} align="right">
|
|
{duration}
|
|
</Typography>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|