Files
reui-blocks/blocks/feature12.tsx
T
2026-07-27 12:56:06 +03:30

187 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import {
ArrowDownToLine,
ArrowUpDown,
Code,
Redo,
Repeat,
Scaling,
Scan,
} from "lucide-react";
import { useEffect, useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent } from "@/components/ui/card";
import type { CarouselApi } from "@/components/ui/carousel";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
import { cn } from "@/lib/utils";
const slides = [
{
title: "سطح دسترسی",
description:
"گزارش سفارشی را بدون SQL بسازید و سریع منتشر کنید.",
icon: <Code className="size-4" />,
},
{
title: "تحلیل پیش‌بین",
description:
"ابعاد مختلف را بچرخانید و الگوی پنهان را پیدا کنید.",
icon: <ArrowUpDown className="size-4" />,
},
{
title: "اشتراک گزارش",
description:
"نمای اجرایی برای تصمیم‌های سریع طراحی کنید.",
icon: <Redo className="size-4" />,
},
{
title: "مدل داده",
description:
"داده پراکنده را در یک مدل واحد ساختاردهی کنید.",
icon: <ArrowDownToLine className="size-4" />,
},
{
title: "گزارش‌ساز",
description:
"گزارش سفارشی را بدون SQL بسازید و سریع منتشر کنید.",
icon: <Repeat className="size-4" />,
},
{
title: "Pivot تعاملی",
description:
"ابعاد مختلف را بچرخانید و الگوی پنهان را پیدا کنید.",
icon: <Scan className="size-4" />,
},
{
title: "داشبورد مدیریتی",
description:
"نمای اجرایی برای تصمیم‌های سریع طراحی کنید.",
icon: <Scaling className="size-4" />,
},
];
interface Feature12Props {
className?: string;
}
const Feature12 = ({ className }: Feature12Props) => {
const [api, setApi] = useState<CarouselApi>();
const [progress, setProgress] = useState(1 / slides.length);
const [currentSlide, setCurrentSlide] = useState(1);
useEffect(() => {
if (!api) {
return;
}
const updateProgress = () => {
setProgress(
Math.max(1 / slides.length, Math.min(1, api.scrollProgress())),
);
};
const updateCurrentSlide = () => {
setCurrentSlide(api.selectedScrollSnap() + 1);
};
updateProgress();
updateCurrentSlide();
api.on("scroll", updateProgress);
api.on("select", updateCurrentSlide);
api.on("reInit", updateCurrentSlide);
return () => {
api.off("scroll", updateProgress);
api.off("select", updateCurrentSlide);
api.off("reInit", updateCurrentSlide);
};
}, [api]);
return (
<section
dir="rtl"
lang="fa"
className={cn("w-full overflow-hidden py-32", className)}
>
<div className="container max-w-7xl min-w-0">
<div className="mb-10 flex min-w-0 flex-col items-center gap-6 md:mb-20">
<Badge variant="outline">داده سازمانی</Badge>
<h2 className="mb-2 max-w-full text-center text-3xl font-semibold text-balance break-words lg:text-2xll">
تصمیم استراتژیک را داده‌محور کنید
</h2>
</div>
<Carousel
className="w-full min-w-0"
setApi={setApi}
opts={{
align: "start",
direction: "rtl",
}}
>
<div className="mb-4 space-y-4 px-1 md:mb-5 lg:flex lg:items-center lg:justify-between lg:space-y-0">
<p className="w-full min-w-0 text-pretty break-words font-medium lg:flex-1 lg:pe-3">
داشبوردهای تعاملی به تیم‌ها اجازه می‌دهد سؤال جدید بپرسند بدون
اینکه هر بار به تیم داده وابسته باشند.
</p>
<div className="flex w-full min-w-0 items-center gap-2 lg:w-auto lg:shrink-0">
<div className="me-2 flex min-w-0 flex-1 items-center gap-3 text-xs text-muted-foreground lg:flex-none">
<span className="shrink-0">
{currentSlide.toLocaleString("fa-IR").padStart(2, "۰")}
</span>
<div className="h-[2px] min-w-0 flex-1 overflow-hidden rounded-full bg-muted lg:w-52 lg:flex-none">
<div
className="h-full origin-start bg-primary transition-transform"
style={{ transform: `scaleX(${progress})` }}
/>
</div>
<span className="shrink-0">
{slides.length.toLocaleString("fa-IR").padStart(2, "۰")}
</span>
</div>
<CarouselPrevious className="static translate-y-0 rtl:rotate-180" />
<CarouselNext className="static translate-y-0 rtl:rotate-180" />
</div>
</div>
<CarouselContent>
{slides.map((slide, index) => (
<CarouselItem
key={index}
className="min-w-0 basis-full md:basis-1/2 lg:basis-1/3"
>
<div className="min-w-0 p-1">
<Card className="min-w-0 overflow-hidden">
<CardContent className="flex min-w-0 flex-col justify-center p-6">
<div className="min-w-0">
<span className="mb-5 flex size-8 items-center justify-center rounded-full bg-accent lg:size-10">
{slide.icon}
</span>
<p className="text-xl font-semibold break-words md:text-xl lg:text-xl">
{slide.title}
</p>
<p className="pt-2 text-pretty break-words text-muted-foreground">
{slide.description}
</p>
</div>
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
</Carousel>
</div>
</section>
);
};
export { Feature12 };