219 lines
7.7 KiB
TypeScript
219 lines
7.7 KiB
TypeScript
"use client";
|
||||
|
|
|
|||
|
|
import { motion } from "framer-motion";
|
|||
|
|
import { Play } from "lucide-react";
|
|||
|
|
|
|||
|
|
import { Button } from "@/components/ui/button";
|
|||
|
|
import { Card } from "@/components/ui/card";
|
|||
|
|
import {
|
|||
|
|
Dialog,
|
|||
|
|
DialogContent,
|
|||
|
|
DialogDescription,
|
|||
|
|
DialogTitle,
|
|||
|
|
DialogTrigger,
|
|||
|
|
} from "@/components/ui/dialog";
|
|||
|
|
import { useIsMobile } from "@/hooks/use-mobile";
|
|||
|
|
import { cn } from "@/lib/utils";
|
|||
|
|
|
|||
|
|
interface Feature313Props {
|
|||
|
|
className?: string;
|
|||
|
|
imageSrc: string;
|
|||
|
|
imageAlt: string;
|
|||
|
|
videoUrl?: string;
|
|||
|
|
videoCaption: string;
|
|||
|
|
videoSecondaryCaption: string;
|
|||
|
|
cards: {
|
|||
|
|
id: string;
|
|||
|
|
title: string;
|
|||
|
|
image: string;
|
|||
|
|
className?: string;
|
|||
|
|
}[];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const Feature313 = ({
|
|||
|
|
className,
|
|||
|
|
imageSrc = "/block-media/shadcnblocks/block/lummi/bw6.jpeg",
|
|||
|
|
imageAlt = "گزارش عملکرد",
|
|||
|
|
videoUrl = "/block-media/shadcnblocks/block/man-1.mp4",
|
|||
|
|
videoCaption = "داده پراکنده را به تصمیم تبدیل کنید",
|
|||
|
|
videoSecondaryCaption = "تحلیل داده",
|
|||
|
|
cards = [
|
|||
|
|
{
|
|||
|
|
id: "growth",
|
|||
|
|
title: "گزارش زمانبندیشده",
|
|||
|
|
image: "/block-media/shadcnblocks/block/placeholder-2.svg",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "design",
|
|||
|
|
title: "تحلیل کوHORT",
|
|||
|
|
image: "/block-media/shadcnblocks/block/placeholder-3.svg",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "support",
|
|||
|
|
title: "مقایسه دورهای",
|
|||
|
|
image: "/block-media/shadcnblocks/block/placeholder-4.svg",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: "partnerships",
|
|||
|
|
title: "خروجی گزارش",
|
|||
|
|
image: "/block-media/shadcnblocks/block/placeholder-5.svg",
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}: Feature313Props) => {
|
|||
|
|
const getYouTubeVideoId = (url: string) => {
|
|||
|
|
const regExp =
|
|||
|
|
/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/;
|
|||
|
|
const match = url.match(regExp);
|
|||
|
|
return match && match[2].length === 11 ? match[2] : null;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const youtubeId = videoUrl ? getYouTubeVideoId(videoUrl) : null;
|
|||
|
|
const isYouTube = !!youtubeId;
|
|||
|
|
const isMobile = useIsMobile();
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<section dir="rtl" lang="fa" className={cn("bg-muted py-24", className)}>
|
|||
|
|
<div className="container flex flex-col gap-2">
|
|||
|
|
<div className="grid grid-cols-1 gap-1 lg:grid-cols-4">
|
|||
|
|
{cards.map((card, index) => (
|
|||
|
|
<Card key={card.id} className="border-none p-10 text-start shadow-none">
|
|||
|
|
<div className="flex items-center justify-between gap-2">
|
|||
|
|
<div className="flex gap-1">
|
|||
|
|
{cards.map((_, circleIndex) => (
|
|||
|
|
<div
|
|||
|
|
key={circleIndex}
|
|||
|
|
className={`${index >= circleIndex ? "bg-primary" : "bg-muted"} size-2 rounded-full`}
|
|||
|
|
/>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
<div className="text-sm font-medium text-muted-foreground">
|
|||
|
|
{(index + 1).toLocaleString("fa-IR").padStart(2, "۰")}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div className="flex flex-col gap-4 md:flex-row md:justify-between">
|
|||
|
|
<div className="aspect-square size-12 shrink-0">
|
|||
|
|
<img
|
|||
|
|
src={card.image}
|
|||
|
|
alt={card.title}
|
|||
|
|
className="aspect-square h-full w-full rounded-lg object-cover"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
<div className="min-w-0 flex-1">
|
|||
|
|
<h3 className="text-md leading-tight font-medium text-pretty">
|
|||
|
|
{card.title}
|
|||
|
|
</h3>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</Card>
|
|||
|
|
))}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<Dialog>
|
|||
|
|
<DialogTrigger asChild>
|
|||
|
|
<motion.div
|
|||
|
|
className="group relative aspect-video min-h-72 cursor-pointer overflow-hidden rounded-lg"
|
|||
|
|
whileHover="hover"
|
|||
|
|
initial="initial"
|
|||
|
|
>
|
|||
|
|
<motion.div
|
|||
|
|
className="absolute inset-0"
|
|||
|
|
variants={{
|
|||
|
|
initial: { filter: "blur(0px)" },
|
|||
|
|
hover: { filter: "blur(4px)" },
|
|||
|
|
}}
|
|||
|
|
transition={{ duration: 0.3, ease: "easeOut" }}
|
|||
|
|
>
|
|||
|
|
<motion.img
|
|||
|
|
src={imageSrc}
|
|||
|
|
alt={imageAlt}
|
|||
|
|
className="h-full w-full rounded-lg object-cover"
|
|||
|
|
variants={{
|
|||
|
|
initial: { scale: 1 },
|
|||
|
|
hover: { scale: 1.1 },
|
|||
|
|
}}
|
|||
|
|
transition={{ duration: 0.3, ease: "easeOut" }}
|
|||
|
|
/>
|
|||
|
|
</motion.div>
|
|||
|
|
<div className="bg-opacity-20 absolute inset-0 flex items-center justify-center">
|
|||
|
|
<motion.div
|
|||
|
|
className="flex flex-col items-center justify-center gap-2 md:flex-row"
|
|||
|
|
variants={{
|
|||
|
|
initial: { gap: "0.5rem" },
|
|||
|
|
hover: { gap: "0rem" },
|
|||
|
|
}}
|
|||
|
|
transition={{ duration: 0.3, ease: "easeOut" }}
|
|||
|
|
>
|
|||
|
|
<motion.div
|
|||
|
|
variants={{
|
|||
|
|
initial: { x: 0, scale: 1 },
|
|||
|
|
hover: {
|
|||
|
|
x: isMobile ? 0 : "-75%",
|
|||
|
|
scale: 1.2,
|
|||
|
|
},
|
|||
|
|
}}
|
|||
|
|
transition={{ duration: 0.3, ease: "easeOut" }}
|
|||
|
|
>
|
|||
|
|
<Button
|
|||
|
|
size="lg"
|
|||
|
|
className="size-14 rounded-full bg-secondary hover:bg-secondary lg:size-20"
|
|||
|
|
>
|
|||
|
|
<Play className="ms-0.5 size-5 text-primary md:size-7" />
|
|||
|
|
<span className="sr-only">{videoCaption}</span>
|
|||
|
|
</Button>
|
|||
|
|
</motion.div>
|
|||
|
|
|
|||
|
|
<motion.div
|
|||
|
|
className="text-center md:text-start"
|
|||
|
|
variants={{
|
|||
|
|
initial: { opacity: 1 },
|
|||
|
|
hover: { opacity: 0, transform: "-translate-y-3" },
|
|||
|
|
}}
|
|||
|
|
transition={{ duration: 0.3, ease: "easeOut" }}
|
|||
|
|
>
|
|||
|
|
<div className="px-4 py-2">
|
|||
|
|
<p className="text-lg font-bold text-secondary">
|
|||
|
|
{videoCaption}
|
|||
|
|
</p>
|
|||
|
|
<p className="text-lg font-medium text-muted-foreground">
|
|||
|
|
{videoSecondaryCaption}
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</motion.div>
|
|||
|
|
</motion.div>
|
|||
|
|
</div>
|
|||
|
|
</motion.div>
|
|||
|
|
</DialogTrigger>
|
|||
|
|
<DialogContent className="w-full border-none bg-black p-0 md:max-w-6xl md:p-1">
|
|||
|
|
<DialogTitle className="sr-only">{videoCaption}</DialogTitle>
|
|||
|
|
<DialogDescription className="sr-only">
|
|||
|
|
{videoSecondaryCaption} {videoCaption}
|
|||
|
|
</DialogDescription>
|
|||
|
|
<div className="aspect-video w-full">
|
|||
|
|
{isYouTube ? (
|
|||
|
|
<iframe
|
|||
|
|
className="h-full w-full"
|
|||
|
|
src={`https://www.youtube.com/embed/${youtubeId}?autoplay=1`}
|
|||
|
|
title="شاخصهای کلیدی را یکجا ببینید"
|
|||
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|||
|
|
allowFullScreen
|
|||
|
|
/>
|
|||
|
|
) : (
|
|||
|
|
<video
|
|||
|
|
className="h-full w-full rounded-lg"
|
|||
|
|
controls
|
|||
|
|
autoPlay
|
|||
|
|
src={videoUrl}
|
|||
|
|
>
|
|||
|
|
ویجتهای موردنیاز هر تیم را بچینید و فقط معیارهای مرتبط را ببینید.
|
|||
|
|
</video>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
</DialogContent>
|
|||
|
|
</Dialog>
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
export { Feature313 };
|