296 lines
9.2 KiB
TypeScript
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 };
|