Files
company-site/components/features/projects/projects-section.tsx
T
2026-09-16 08:47:12 +03:30

344 lines
14 KiB
TypeScript

"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import {
Landmark,
Banknote,
BarChart3,
ShieldCheck,
Workflow,
BadgeCheck,
Terminal,
ArrowLeft,
} from "lucide-react";
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: "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: "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: "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: "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",
},
];
/** کامپوننت تصویر پروژه */
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
id="projects"
className="relative py-24 md:py-32 bg-gray-950"
aria-labelledby="projects-heading"
>
<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="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: "-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-indigo-400"></span>
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
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>
{/* ── نمای 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>
<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>
<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>
);
}