Files
reui-blocks/blocks/feature216.tsx
T
2026-08-13 20:38:45 +03:30

296 lines
9.2 KiB
TypeScript

"use client";
import type { LucideIcon } from "lucide-react";
import {
ArrowLeft,
ArrowRight,
Brain,
Circle,
ClipboardList,
MessageSquare,
Rocket,
Share2,
Users,
} from "lucide-react";
import { startTransition, useEffect, useRef, useState } from "react";
import { AspectRatio } from "@/components/ui/aspect-ratio";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { cn } from "@/lib/utils";
const TAB_ITEMS = [
{
id: "team-sync",
value: "team_sync",
name: "داده پراکنده را به تصمیم تبدیل کنید",
icon: Users,
color: "#2563EB",
image: {
src: "/block-media/shadcnblocks/block/photos/redd-f-5U_28ojjgms-unsplash.jpg",
alt: "گزارش عملکرد",
},
summary: "ویجت‌های موردنیاز هر تیم را بچینید و فقط معیارهای مرتبط را ببینید.",
link: "#",
},
{
id: "discussions",
value: "discussions",
name: "تحلیل داده",
icon: MessageSquare,
color: "#F97316",
image: {
src: "/block-media/shadcnblocks/block/photos/annie-spratt-hCb3lIB8L8E-unsplash.jpg",
alt: "داشبورد تحلیل داده",
},
summary: "داده را بر اساس محصول، منطقه یا کانال برش دهید و الگو پیدا کنید.",
link: "#",
},
{
id: "knowledge-share",
value: "knowledge_share",
name: "داشبورد",
icon: Share2,
color: "#A855F7",
image: {
src: "/block-media/shadcnblocks/block/photos/annie-spratt-MChSQHxGZrQ-unsplash.jpg",
alt: "نمودار شاخص کلیدی",
},
summary: "تغییرات مهم را در طول زمان دنبال کنید و نقطه عطف را شناسایی کنید.",
link: "#",
},
{
id: "brainstorming",
value: "brainstorming",
name: "شاخص کلیدی",
icon: Brain,
color: "#10B981",
image: {
src: "/block-media/shadcnblocks/block/photos/dylan-gillis-KdeqA3aTnBY-unsplash.jpg",
alt: "گزارش عملکرد",
},
summary: "وقتی شاخص از حد مجاز گذشت، فوراً مطلع شوید.",
link: "#",
},
{
id: "task-tracking",
value: "task_tracking",
name: "بینش لحظه‌ای",
icon: ClipboardList,
color: "#EF4444",
image: {
src: "/block-media/shadcnblocks/block/photos/studio-republic-fotKKqWNMQ4-unsplash.jpg",
alt: "داشبورد تحلیل داده",
},
summary: "ویجت‌های موردنیاز هر تیم را بچینید و فقط معیارهای مرتبط را ببینید.",
link: "#",
},
{
id: "launch-prep",
value: "launch_prep",
name: "تحلیل داده",
icon: Rocket,
color: "#EAB308",
image: {
src: "/block-media/shadcnblocks/block/photos/annie-spratt-vGgn0xLdy8s-unsplash.jpg",
alt: "نمودار شاخص کلیدی",
},
summary: "داده را بر اساس محصول، منطقه یا کانال برش دهید و الگو پیدا کنید.",
link: "#",
},
] as const;
type TabValue = (typeof TAB_ITEMS)[number]["value"];
interface TabLabelProps {
value: string;
}
const TabLabel = ({ value }: TabLabelProps) => {
const [label, setLabel] = useState<{
name: string;
icon: LucideIcon;
color: string;
}>({
name: "",
icon: Circle,
color: "",
});
useEffect(() => {
const tab = TAB_ITEMS.find((trigger) => trigger.value === value);
if (!tab) return;
startTransition(() => {
setLabel({
name: tab.name,
icon: tab.icon,
color: tab.color,
});
});
}, [value]);
const Icon = label.icon;
return (
<div className="mb-8 flex items-center justify-center gap-6 xl:hidden">
<div
className="flex size-8 rounded-md"
style={{ backgroundColor: label.color }}
>
<Icon className="m-auto stroke-white" />
</div>
<div className="text-lg">{label.name}</div>
</div>
);
};
interface Feature216Props {
className?: string;
}
const Feature216 = ({ className }: Feature216Props) => {
const [buttonPosition, setButtonPosition] = useState<number>(0);
// Rightmost tab in RTL (`justify-start`) is the first item.
const [tabValue, setTabValue] = useState<TabValue>(TAB_ITEMS[0].value);
const imageRef = useRef<HTMLDivElement | null>(null);
const updateBottom = () => {
if (imageRef.current) {
const height = imageRef.current.getBoundingClientRect().height / 2;
startTransition(() => {
setButtonPosition(height);
});
}
};
useEffect(() => {
updateBottom();
window.addEventListener("resize", updateBottom);
return () => {
window.removeEventListener("resize", updateBottom);
};
}, []);
const getCurrentTabIndex = () => {
return TAB_ITEMS.findIndex((elem) => elem.value === tabValue);
};
const handleTabNavigation = (direction: "next" | "prev") => {
const currentIndex = getCurrentTabIndex();
if (currentIndex === -1) return;
const nextIndex =
direction === "next" ? currentIndex + 1 : currentIndex - 1;
const isInBounds = nextIndex >= 0 && nextIndex < TAB_ITEMS.length;
if (isInBounds) {
setTabValue(TAB_ITEMS[nextIndex].value);
}
};
const onValueChange = (value: string) => {
setTabValue(value as TabValue);
};
const currentIndex = getCurrentTabIndex();
return (
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
<div className="container">
<h2 className="mb-10 text-center text-3xl font-light text-balance md:text-xl">
شاخص‌های کلیدی را یکجا ببینید
</h2>
<Tabs
dir="rtl"
value={tabValue}
onValueChange={onValueChange}
className="w-full"
>
<TabsList className="hidden !h-auto w-full !justify-start items-end gap-12 bg-transparent pb-10 xl:flex">
{TAB_ITEMS.map((trigger, index) => {
const Icon = trigger.icon;
return (
<TabsTrigger
className="flex !h-auto !flex-none shrink-0 flex-col items-center gap-5 !border-none !bg-transparent p-0 opacity-[0.2] !shadow-none grayscale hover:!bg-transparent hover:opacity-50 hover:grayscale-0 data-[state=active]:!bg-transparent data-[state=active]:opacity-100 data-[state=active]:!shadow-none data-[state=active]:grayscale-0 dark:!bg-transparent dark:data-[state=active]:!bg-transparent"
key={trigger.id}
value={trigger.value}
data-index={index}
>
<div
className="flex size-8 rounded-md"
style={{ backgroundColor: trigger.color }}
>
<Icon className="m-auto stroke-white" />
</div>
<div className="text-xl font-normal">{trigger.name}</div>
</TabsTrigger>
);
})}
</TabsList>
<div className="relative">
{TAB_ITEMS.map((tab) => (
<TabsContent
key={tab.id}
value={tab.value}
className="animate-in duration-300 ease-in-out fade-in"
>
<TabLabel value={tab.value} />
<div className="w-full" ref={imageRef}>
<AspectRatio
ratio={1.847058824}
className="w-full overflow-hidden rounded-lg border shadow-xl"
>
<img
src={tab.image.src}
alt={tab.image.alt}
className="block size-full object-cover object-center"
/>
</AspectRatio>
</div>
<p className="mt-4 text-center text-sm text-pretty text-muted-foreground">
{tab.summary}
</p>
</TabsContent>
))}
{/* Physical left (end in RTL): previous — moves toward rightmost tabs */}
<div
className="absolute end-0 translate-y-1/2"
style={{ bottom: buttonPosition }}
>
<Button
variant="secondary"
disabled={currentIndex <= 0}
onClick={() => handleTabNavigation("prev")}
size="lg"
className="size-12 translate-x-1/2 rounded-full border shadow-lg rtl:-translate-x-1/2"
>
<ArrowRight />
</Button>
</div>
{/* Physical right (start in RTL): next — moves toward leftmost tabs */}
<div
className="absolute start-0 translate-y-1/2"
style={{ bottom: buttonPosition }}
>
<Button
variant="secondary"
disabled={currentIndex >= TAB_ITEMS.length - 1}
onClick={() => handleTabNavigation("next")}
size="lg"
className="size-12 -translate-x-1/2 rounded-full border shadow-lg rtl:translate-x-1/2"
>
<ArrowLeft />
</Button>
</div>
</div>
</Tabs>
</div>
</section>
);
};
export { Feature216 };