173 lines
5.8 KiB
TypeScript
173 lines
5.8 KiB
TypeScript
"use client";
|
||
|
||
import { motion, useInView } from "framer-motion";
|
||
import { CornerDownRight } from "lucide-react";
|
||
import React, { useEffect, useRef, useState } from "react";
|
||
|
||
import { Button } from "@/components/ui/button";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
// Custom hook to get previous value
|
||
const usePrevious = <T,>(value: T): T | undefined => {
|
||
const [prev, setPrev] = useState<T | undefined>(undefined);
|
||
const ref = useRef(value);
|
||
|
||
useEffect(() => {
|
||
setPrev(ref.current);
|
||
ref.current = value;
|
||
}, [value]);
|
||
|
||
return prev;
|
||
};
|
||
|
||
interface Process2Props {
|
||
className?: string;
|
||
}
|
||
|
||
const Process2 = ({ className }: Process2Props) => {
|
||
const process = [
|
||
{
|
||
step: "۱",
|
||
title: "کشف و تحقیق",
|
||
image: "/block-media/shadcnblocks/block/guri4/img11.png",
|
||
description:
|
||
"با درک اهداف کسبوکار، مخاطب هدف و چالشهای فعلی شما شروع میکنیم. این فاز شامل تحقیق، تحلیل و برنامهریزی استراتژیک برای شناسایی فرصتهاست.",
|
||
},
|
||
{
|
||
step: "۲",
|
||
title: "استراتژی و برنامهریزی",
|
||
image: "/block-media/shadcnblocks/block/guri4/img12.png",
|
||
description:
|
||
"بر اساس یافتهها، استراتژی جامعی متناسب با اهداف شما توسعه میدهیم. این شامل تعریف رویکرد، زمانبندی و نقاط عطف کلیدی برای موفقیت است.",
|
||
},
|
||
{
|
||
step: "۰۳",
|
||
title: "اجرا و توسعه",
|
||
image: "/block-media/shadcnblocks/block/guri4/img10.png",
|
||
description:
|
||
"استراتژی را از طریق پیادهسازی و توسعه دقیق به واقعیت تبدیل میکنیم. تیم ما بهصورت همکاری کار میکند تا هر جزئیات با نیازها و استانداردهای شما همخوان باشد.",
|
||
},
|
||
{
|
||
step: "۰۴",
|
||
title: "بهینهسازی و بهبود",
|
||
image: "/block-media/shadcnblocks/block/guri4/img9.png",
|
||
description:
|
||
"عملکرد را بهطور مستمر پایش و بازخورد جمعآوری میکنیم تا راهحل را پالایش و بهبود دهیم. این فرآیند تکراری موفقیت و رشد بلندمدت را تضمین میکند.",
|
||
},
|
||
];
|
||
|
||
const [active, setActive] = useState<number>(0);
|
||
const previousActive = usePrevious(active);
|
||
|
||
return (
|
||
<section dir="rtl" lang="fa" className={cn("py-32", className)}>
|
||
<div className="container">
|
||
<div className="grid grid-cols-1 gap-5 lg:grid-cols-2 lg:gap-20">
|
||
<div className="top-10 h-fit w-fit gap-3 space-y-7 py-8 lg:sticky">
|
||
<h1 className="relative w-fit text-2xll font-semibold tracking-tight lg:text-2xll">
|
||
فرآیند ما
|
||
</h1>
|
||
<p className="text-start text-base text-foreground/50">
|
||
از کشف نیازهای شما تا تحویل نهایی، هر مرحله با دقت و شفافیت پیش
|
||
میرود تا بهترین نتیجه حاصل شود.
|
||
</p>
|
||
<div className="relative h-90 overflow-hidden border">
|
||
{previousActive !== undefined && (
|
||
<div className="absolute top-0 h-full w-full">
|
||
<img
|
||
src={process[previousActive].image}
|
||
className="h-full w-full object-cover"
|
||
alt={process[previousActive].title}
|
||
/>
|
||
</div>
|
||
)}
|
||
<motion.div
|
||
initial={{ clipPath: "inset(0% 0% 0% 100%)" }}
|
||
animate={{ clipPath: "inset(0% 0% 0% 0%)" }}
|
||
key={active}
|
||
transition={{
|
||
type: "spring",
|
||
stiffness: 150,
|
||
damping: 20,
|
||
}}
|
||
className="h-full w-full"
|
||
>
|
||
<img
|
||
src={process[active].image}
|
||
className="h-full w-full object-cover"
|
||
alt={process[active].title}
|
||
/>
|
||
</motion.div>
|
||
</div>
|
||
<Button
|
||
variant="ghost"
|
||
className="flex items-center justify-start gap-2"
|
||
>
|
||
<CornerDownRight className="text-orange-500 rtl:rotate-180" />
|
||
تماس بگیرید
|
||
</Button>
|
||
</div>
|
||
<ul className="relative w-full lg:ps-22">
|
||
{process.map((step, index) => (
|
||
<ProcessCard
|
||
key={index}
|
||
step={step}
|
||
index={index}
|
||
setActive={setActive}
|
||
/>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
};
|
||
|
||
const ProcessCard = ({
|
||
step,
|
||
index,
|
||
setActive,
|
||
}: {
|
||
step: {
|
||
step: string;
|
||
title: string;
|
||
image: string;
|
||
description: string;
|
||
};
|
||
index: number;
|
||
setActive: (index: number) => void;
|
||
}) => {
|
||
const ref = useRef<HTMLLIElement>(null);
|
||
|
||
const itemInView = useInView(ref, {
|
||
amount: 0,
|
||
margin: "0px 0px -60% 0px",
|
||
});
|
||
|
||
useEffect(() => {
|
||
if (itemInView) {
|
||
setActive(index);
|
||
}
|
||
}, [itemInView, index, setActive]);
|
||
|
||
return (
|
||
<li
|
||
ref={ref}
|
||
key={index}
|
||
className="relative flex flex-col justify-between gap-12 border-b py-8 lg:py-16"
|
||
>
|
||
<div className="flex w-fit items-center justify-center px-4 py-1 text-9xl tracking-tighter">
|
||
{step.step}
|
||
</div>
|
||
<div>
|
||
<h3 className="mb-4 text-start text-xl font-semibold tracking-tighter lg:text-3xl">
|
||
{step.title}
|
||
</h3>
|
||
<p className="text-start text-foreground/50">{step.description}</p>
|
||
</div>
|
||
</li>
|
||
);
|
||
};
|
||
|
||
export { Process2 };
|