344 lines
14 KiB
TypeScript
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>
|
|
);
|
|
}
|