a
This commit is contained in:
@@ -1,134 +1,342 @@
|
||||
"use client";
|
||||
|
||||
import { motion } from "framer-motion";
|
||||
import Image from "next/image";
|
||||
import { useState } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import {
|
||||
Landmark,
|
||||
Banknote,
|
||||
BarChart3,
|
||||
ShieldCheck,
|
||||
Workflow,
|
||||
BadgeCheck,
|
||||
Terminal,
|
||||
ArrowLeft,
|
||||
} from "lucide-react";
|
||||
|
||||
// دیتای تستی پروژهها (میتوانید با دیتای واقعی جایگزین کنید)
|
||||
const projects = [
|
||||
type ProjectStatus = "در حال بهرهبرداری" | "در حال توسعه" | "فاز آزمایشی";
|
||||
|
||||
interface Project {
|
||||
id: string;
|
||||
title: string;
|
||||
domain: string;
|
||||
status: ProjectStatus;
|
||||
description: string;
|
||||
stack: string[];
|
||||
metrics: { label: string; value: string }[];
|
||||
hasDetails: boolean;
|
||||
Icon: typeof Landmark;
|
||||
image: string;
|
||||
}
|
||||
|
||||
const customEase: [number, number, number, number] = [0.16, 1, 0.3, 1];
|
||||
|
||||
const projects: Project[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "سیستم مدیریت مالی فراز",
|
||||
category: "نرمافزار سازمانی",
|
||||
description: "یک پلتفرم یکپارچه برای مدیریت منابع مالی سازمانها با داشبوردهای تحلیلی در لحظه.",
|
||||
image: "/project-1.jpg", // این عکسها را در پوشه public قرار دهید
|
||||
color: "from-blue-500/20 to-purple-500/20",
|
||||
id: "core-banking-report",
|
||||
title: "دستیار آموزشی و معلم هوشمند",
|
||||
domain: "آموزشی / سازمانی",
|
||||
status: "در حال بهرهبرداری",
|
||||
description:
|
||||
"این سیستم به جای ارائه پاسخهای عمومی، تمامی کتب درسی را در پایگاه دانش خود بلعیده و پردازش کرده است. نتیجه؟ یک معلم خصوصی بینقص که ۲۴ ساعته در دسترس است، دقیقا بر اساس متن کتاب درسی آموزش میدهد، امتحان میگیرد، تصحیح میکند و با دانشآموز به صورت صوتی صحبت میکند.",
|
||||
stack: ["Next.js", "TTS", "RAG", "Voice-Enabled Learning", "Speech-to-Text"],
|
||||
metrics: [
|
||||
{ label: "زمان تولید گزارش شبانه", value: "۴ دقیقه" },
|
||||
{ label: "حجم پردازش روزانه", value: "۲.۱ میلیون رکورد" },
|
||||
],
|
||||
hasDetails: true,
|
||||
Icon: Landmark,
|
||||
image: "/image/student2.png",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "اپلیکیشن سلامتجو",
|
||||
category: "اپلیکیشن موبایل",
|
||||
description: "سیستم نوبتدهی و پایش سلامت بیماران با قابلیت یکپارچهسازی با گجتهای پوشیدنی.",
|
||||
image: "/project-2.jpg",
|
||||
color: "from-emerald-500/20 to-teal-500/20",
|
||||
id: "treasury-dashboard",
|
||||
title: "دستیار هوشمند فروش و پشتیبانی",
|
||||
domain: "سازمانی / مالی",
|
||||
status: "در حال بهرهبرداری",
|
||||
description:
|
||||
"راهکاری نوین برای کسبوکارهای آنلاین است که شکاف میان کاربر و محتوای سایت شما را پر میکند. این سیستم با خزش (Crawling) و ایندکسگذاری هوشمند تمامی صفحات وبسایت شما، به یک متخصصِ تمامعیار تبدیل میشود که تمامی محصولات، خدمات، قیمتها و مستندات شما را میشناسد.",
|
||||
stack: ["Next.js", "TypeScript", "NestJS", "Redis", "SQL Server"],
|
||||
metrics: [
|
||||
{ label: "کاربر همزمان", value: "۳۵۰+" },
|
||||
{ label: "Uptime سهماهه", value: "۹۹.۹۵٪" },
|
||||
],
|
||||
hasDetails: true,
|
||||
Icon: Banknote,
|
||||
image: "/image/payai.png",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "پلتفرم فروشگاهی نوینشاپ",
|
||||
category: "تجارت الکترونیک",
|
||||
description: "فروشگاهساز ابری با معماری میکروسرویس و قابلیت مقیاسپذیری بالا برای ترافیکهای سنگین.",
|
||||
image: "/project-3.jpg",
|
||||
color: "from-orange-500/20 to-red-500/20",
|
||||
id: "fraud-detection",
|
||||
title: "مرکز تماس هوشمند",
|
||||
domain: "بانکی / پشتیبانی",
|
||||
status: "در حال توسعه",
|
||||
description:
|
||||
"«مرکز تماس هوشمند» فراتر از یک سیستم ضبط مکالمه است. این پلتفرم با استفاده از فناوریهای پیشرفته Speech-to-Text (STT) و LLM، تمامی تماسهای ورودی و خروجی مرکز تماس شما را تحلیل کرده و شاخصهای کلیدی عملکرد (KPI) را به صورت خودکار استخراج میکند.",
|
||||
stack: ["Python", "FastAPI", "PostgreSQL", "Kafka", "Docker"],
|
||||
metrics: [
|
||||
{ label: "زمان امتیازدهی هر تراکنش", value: "< ۴۰ میلیثانیه" },
|
||||
{ label: "هشدارهای نادرست", value: "کاهش ۳۱٪" },
|
||||
],
|
||||
hasDetails: true,
|
||||
Icon: ShieldCheck,
|
||||
image: "/image/aicall2.png",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "اتوماسیون اداری هوشمند",
|
||||
category: "هوش مصنوعی",
|
||||
description: "سامانه اتوماسیون مبتنی بر هوش مصنوعی برای دستهبندی خودکار نامهها و ارجاعات.",
|
||||
image: "/project-4.jpg",
|
||||
color: "from-indigo-500/20 to-blue-500/20",
|
||||
id: "bpm-automation",
|
||||
title: "اپلیکیشن ورزشی نئوفیت",
|
||||
domain: "ورزشی / آموزشی",
|
||||
status: "در حال بهرهبرداری",
|
||||
description:
|
||||
"دستیار هوشمند شما برای رسیدن به تناسب اندام ایدهآل است. این اپلیکیشن با تحلیل دقیق ویژگیهای جسمانی، سبک زندگی و شرایط محیطی، برنامه تمرینی کاملاً اختصاصی برای شما تدوین میکند. با نیوفیت، میتوانید زمان و مکان تمرین را به سادگی تعیین کنید.",
|
||||
stack: ["Next.js", "Node.js", "PostgreSQL", "RabbitMQ"],
|
||||
metrics: [
|
||||
{ label: "فرایند فعال", value: "۱۲ فرایند" },
|
||||
{ label: "کاهش زمان تأییدیه", value: "۴۵٪" },
|
||||
],
|
||||
hasDetails: true,
|
||||
Icon: Workflow,
|
||||
image: "/image/neo.png",
|
||||
},
|
||||
];
|
||||
|
||||
// منحنی انیمیشن لوکس (همان که در Hero استفاده کردیم)
|
||||
const customEase: [number, number, number, number] = [0.16, 1, 0.3, 1];
|
||||
/** کامپوننت تصویر پروژه */
|
||||
function ProjectImage({ project, index }: { project: Project; index: number }) {
|
||||
return (
|
||||
<div
|
||||
className="relative h-40 md:h-44 overflow-hidden border-b border-white/5 bg-neutral-950/60 group"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<img
|
||||
src={project.image}
|
||||
alt={project.title}
|
||||
className="w-full h-full object-cover opacity-60 transition-transform duration-700 group-hover:scale-105 group-hover:opacity-80"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-neutral-900/90 via-neutral-900/20 to-transparent" />
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StatusBadge({ status }: { status: ProjectStatus }) {
|
||||
const tone =
|
||||
status === "در حال بهرهبرداری"
|
||||
? "border-emerald-400/25 bg-emerald-400/10 text-emerald-300"
|
||||
: status === "در حال توسعه"
|
||||
? "border-indigo-400/25 bg-indigo-400/10 text-indigo-300"
|
||||
: "border-amber-400/25 bg-amber-400/10 text-amber-300";
|
||||
return (
|
||||
<span
|
||||
className={`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-[11px] font-medium ${tone}`}
|
||||
>
|
||||
{status === "در حال بهرهبرداری" && (
|
||||
<BadgeCheck className="h-3 w-3" aria-hidden="true" />
|
||||
)}
|
||||
{status}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Projects() {
|
||||
const [activeId, setActiveId] = useState<string>(projects[0].id);
|
||||
const activeProject =
|
||||
projects.find((p) => p.id === activeId) ?? projects[0];
|
||||
const activeIndex = projects.findIndex((p) => p.id === activeProject.id);
|
||||
|
||||
return (
|
||||
<section
|
||||
className="relative min-h-screen py-32 flex flex-col items-center justify-center overflow-hidden bg-gray-950"
|
||||
onMouseMove={(e) => {
|
||||
const { currentTarget, clientX, clientY } = e;
|
||||
const { left, top } = currentTarget.getBoundingClientRect();
|
||||
currentTarget.style.setProperty("--mouse-x", `${clientX - left}px`);
|
||||
currentTarget.style.setProperty("--mouse-y", `${clientY - top}px`);
|
||||
}}
|
||||
id="projects"
|
||||
className="relative py-24 md:py-32 bg-gray-950"
|
||||
aria-labelledby="projects-heading"
|
||||
>
|
||||
{/* افکت Spotlight و Grid (دقیقا مشابه Hero) */}
|
||||
<div className="pointer-events-none absolute -inset-px opacity-50 before:absolute before:inset-0 before:bg-[radial-gradient(800px_circle_at_var(--mouse-x)_var(--mouse-y),rgba(255,255,255,0.06),transparent_40%)]" />
|
||||
<div className="pointer-events-none absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:24px_24px]" />
|
||||
<div className="pointer-events-none absolute inset-0 bg-[linear-gradient(to_right,#80808010_1px,transparent_1px),linear-gradient(to_bottom,#80808010_1px,transparent_1px)] bg-[size:24px_24px]" />
|
||||
|
||||
<div className="z-10 w-full max-w-7xl mx-auto px-4 md:px-8">
|
||||
{/* هدر بخش پروژهها */}
|
||||
<div className="mb-20 md:mb-32">
|
||||
<div className="relative z-10 w-full max-w-7xl mx-auto px-4 md:px-8">
|
||||
{/* هدر سکشن */}
|
||||
<div className="mb-12 md:mb-16">
|
||||
<motion.h2
|
||||
id="projects-heading"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 1, ease: customEase }}
|
||||
className="text-4xl md:text-6xl font-bold tracking-tight text-white mb-6"
|
||||
viewport={{ once: true, margin: "-80px" }}
|
||||
transition={{ duration: 0.9, delay: 0.05, ease: customEase }}
|
||||
className="text-2xl md:text-4xl font-bold tracking-tight text-white mb-5"
|
||||
>
|
||||
خلق تجربههای <span className="text-neutral-500">ماندگار.</span>
|
||||
خلق ارزش از مسیر فناوری <span className="text-indigo-400"></span>
|
||||
</motion.h2>
|
||||
<motion.p
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 1, delay: 0.1, ease: customEase }}
|
||||
className="text-lg md:text-xl text-neutral-400 max-w-2xl"
|
||||
viewport={{ once: true, margin: "-80px" }}
|
||||
transition={{ duration: 0.9, delay: 0.1, ease: customEase }}
|
||||
className="text-base md:text-lg text-neutral-400 max-w-2xl leading-relaxed"
|
||||
>
|
||||
برخی از پروژههای منتخب ما که با تمرکز بر کارایی، زیبایی و معماری قدرتمند توسعه یافتهاند.
|
||||
نگاهی به محصولاتی که مسیر رشد کسبوکارها را هموار کردند
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* گرید پروژهها */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8">
|
||||
{projects.map((project, index) => (
|
||||
<motion.div
|
||||
key={project.id}
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{
|
||||
duration: 1.2,
|
||||
delay: index * 0.15, // انیمیشن آبشاری برای کارتها
|
||||
ease: customEase,
|
||||
}}
|
||||
className="group relative rounded-3xl bg-neutral-900/50 border border-white/5 overflow-hidden backdrop-blur-sm transition-colors hover:bg-neutral-900/80"
|
||||
>
|
||||
{/* بخش تصویر (بالای کارت) */}
|
||||
<div className={`relative h-64 md:h-80 w-full overflow-hidden bg-gradient-to-br ${project.color}`}>
|
||||
{/*
|
||||
نکته: برای استفاده از Image در Next.js، کامنت زیر را بردارید و عکسها را در فولدر public بگذارید.
|
||||
در اینجا موقتاً از یک گرادیانت به عنوان جایگزین عکس استفاده شده است تا خطا نگیرید.
|
||||
*/}
|
||||
{/* <Image
|
||||
src={project.image}
|
||||
alt={project.title}
|
||||
fill
|
||||
className="object-cover object-center transition-transform duration-700 group-hover:scale-105"
|
||||
/> */}
|
||||
|
||||
{/* یک المان تزئینی به جای عکس واقعی (موقتی) */}
|
||||
<div className="absolute inset-0 flex items-center justify-center text-white/20 font-bold text-3xl transition-transform duration-700 group-hover:scale-105">
|
||||
تصویر پروژه
|
||||
</div>
|
||||
</div>
|
||||
{/* ── نمای Master-Detail ── */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 32 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-80px" }}
|
||||
transition={{ duration: 0.9, ease: customEase }}
|
||||
className="grid grid-cols-1 lg:grid-cols-[320px_1fr] gap-5 md:gap-6 items-stretch"
|
||||
role="tablist"
|
||||
aria-label="فهرست پروژهها"
|
||||
>
|
||||
{/* ── Master: فهرست پروژهها ── */}
|
||||
<div
|
||||
className="flex flex-col rounded-2xl border border-white/10 bg-neutral-900/50 overflow-hidden"
|
||||
dir="rtl"
|
||||
>
|
||||
<div className="flex items-center gap-2 border-b border-white/5 bg-neutral-950/60 px-4 py-3">
|
||||
<Terminal className="h-4 w-4 text-indigo-400" aria-hidden="true" />
|
||||
<span
|
||||
className="font-mono text-[11px] text-neutral-400 tracking-wider"
|
||||
dir="ltr"
|
||||
>
|
||||
پروژهها
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* بخش محتوا (پایین کارت) */}
|
||||
<div className="p-8">
|
||||
<div className="text-xs font-semibold tracking-wider text-neutral-500 mb-3 uppercase">
|
||||
{project.category}
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-neutral-100 mb-3 group-hover:text-white transition-colors">
|
||||
{project.title}
|
||||
</h3>
|
||||
<p className="text-neutral-400 leading-relaxed text-sm md:text-base">
|
||||
{project.description}
|
||||
</p>
|
||||
<ul className="flex flex-col p-2 gap-1.5">
|
||||
{projects.map((project, index) => {
|
||||
const isActive = project.id === activeProject.id;
|
||||
return (
|
||||
<li key={project.id}>
|
||||
<button
|
||||
type="button"
|
||||
role="tab"
|
||||
id={`project-tab-${project.id}`}
|
||||
aria-selected={isActive}
|
||||
aria-controls={`project-panel-${project.id}`}
|
||||
onClick={() => setActiveId(project.id)}
|
||||
className={`group w-full text-right rounded-xl border px-3.5 py-3.5 transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[lab(44.0605%_29.0279_-86.0352_/_0.6)] focus-visible:ring-offset-2 focus-visible:ring-offset-gray-950 ${
|
||||
isActive
|
||||
? "border-[lab(44.0605%_29.0279_-86.0352_/_0.4)] bg-[lab(44.0605%_29.0279_-86.0352_/_0.1)]"
|
||||
: "border-transparent hover:border-white/10 hover:bg-neutral-950/50"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border font-mono text-[10px] transition-colors ${
|
||||
isActive
|
||||
? "border-[lab(44.0605%_29.0279_-86.0352_/_0.4)] bg-[lab(44.0605%_29.0279_-86.0352_/_0.15)] text-[lab(44.0605%_29.0279_-86.0352)]"
|
||||
: "border-white/10 bg-neutral-950/60 text-neutral-500 group-hover:text-neutral-300"
|
||||
}`}
|
||||
dir="ltr"
|
||||
>
|
||||
{String(index + 1).padStart(2, "0")}
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div
|
||||
className={`truncate text-sm font-semibold transition-colors ${
|
||||
isActive
|
||||
? "text-white"
|
||||
: "text-neutral-200 group-hover:text-white"
|
||||
}`}
|
||||
>
|
||||
{project.title}
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-0.5 truncate font-mono text-[10px] uppercase tracking-wider text-neutral-500">
|
||||
{project.domain}
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={`h-1.5 w-1.5 shrink-0 rounded-full transition-all ${
|
||||
isActive
|
||||
? "bg-[lab(44.0605%_29.0279_-86.0352)] shadow-[0_0_8px_2px_lab(44.0605%_29.0279_-86.0352_/_0.5)]"
|
||||
: "bg-white/15"
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<div className="mt-auto border-t border-white/5 bg-neutral-950/60 px-4 py-3">
|
||||
<span className="font-mono text-[10px] text-neutral-600" dir="ltr">
|
||||
ژرفا نوآوری داده و آینده
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Detail: پنل پیشنمایش ── */}
|
||||
<div
|
||||
role="tabpanel"
|
||||
id={`project-panel-${activeProject.id}`}
|
||||
aria-labelledby={`project-tab-${activeProject.id}`}
|
||||
className="min-w-0"
|
||||
>
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.article
|
||||
key={activeProject.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -16 }}
|
||||
transition={{ duration: 0.45, ease: customEase }}
|
||||
className="group relative flex flex-col h-full rounded-2xl border border-white/10 bg-neutral-900/50 overflow-hidden"
|
||||
>
|
||||
<ProjectImage project={activeProject} index={activeIndex} />
|
||||
|
||||
<div className="flex flex-col flex-1 p-6 md:p-7">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2 mb-4">
|
||||
<span className="font-mono text-[11px] uppercase tracking-wider text-neutral-500">
|
||||
{activeProject.domain}
|
||||
</span>
|
||||
<StatusBadge status={activeProject.status} />
|
||||
</div>
|
||||
|
||||
<h3 className="text-lg md:text-xl font-bold text-neutral-100 mb-2.5 transition-colors group-hover:text-white">
|
||||
{activeProject.title}
|
||||
</h3>
|
||||
|
||||
<p className="text-sm text-neutral-400 leading-relaxed mb-5">
|
||||
{activeProject.description}
|
||||
</p>
|
||||
|
||||
<ul
|
||||
className="flex flex-wrap gap-1.5 mb-5"
|
||||
aria-label="استک فنی پروژه"
|
||||
>
|
||||
{activeProject.stack.map((tech) => (
|
||||
<li
|
||||
key={tech}
|
||||
dir="ltr"
|
||||
className="rounded-md border border-white/10 bg-neutral-950/60 px-2 py-1 font-mono text-[11px] text-neutral-300"
|
||||
>
|
||||
{tech}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<dl className="mt-auto grid grid-cols-2 gap-3 mb-5">
|
||||
{activeProject.metrics.map((m) => (
|
||||
<div
|
||||
key={m.label}
|
||||
className="rounded-lg border border-white/5 bg-neutral-950/40 px-3 py-2.5"
|
||||
>
|
||||
<dt className="text-[11px] text-neutral-500 leading-snug">
|
||||
{m.label}
|
||||
</dt>
|
||||
<dd
|
||||
className="mt-1 text-sm font-semibold text-indigo-300"
|
||||
dir="auto"
|
||||
>
|
||||
{m.value}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
|
||||
{/* دکمه جزئیات پروژه به صورت همیشگی برای همه موارد */}
|
||||
|
||||
</div>
|
||||
</motion.article>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user