This commit is contained in:
Yolando
2026-03-28 19:48:52 +07:00
parent 0a25960e8f
commit 5b0254d71b
19 changed files with 2016 additions and 1102 deletions

View File

@@ -0,0 +1,224 @@
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { AnimatedSection } from "@/shared/components/animated-section";
import { SectionHeading } from "@/shared/components/section-heading";
import {
ExternalLink,
GitFork,
TrendingUp,
Server,
Smartphone,
Globe,
Layers,
} from "lucide-react";
interface Project {
id: string;
title: string;
description: string;
category: "backend" | "frontend" | "mobile";
tags: string[];
metrics?: string;
repoUrl?: string;
liveUrl?: string;
}
const projects: Project[] = [
{
id: "1",
title: "Core Banking API Gateway",
description:
"High-performance API Gateway handling 500K+ daily transactions with rate limiting, circuit breaker pattern, and distributed tracing across 12 microservices.",
category: "backend",
tags: ["Spring Boot", "Kafka", "Redis", "Docker"],
metrics: "Increased throughput by 40%, 99.9% uptime",
},
{
id: "2",
title: "Payment Processing Engine",
description:
"Event-driven payment system with Saga pattern for distributed transactions, supporting real-time transfers, bill payments, and batch processing.",
category: "backend",
tags: ["Java", "Kafka", "PostgreSQL", "gRPC"],
metrics: "Processing 200K+ payments/day",
},
{
id: "3",
title: "Customer Onboarding Portal",
description:
"Modern onboarding portal with multi-step KYC verification, document upload, and real-time status tracking for banking customers.",
category: "frontend",
tags: ["React", "Next.js", "TypeScript", "Tailwind"],
repoUrl: "#",
liveUrl: "#",
},
{
id: "4",
title: "Internal Dashboard",
description:
"Admin dashboard for monitoring API performance, user analytics, and system health with real-time WebSocket updates.",
category: "frontend",
tags: ["React", "Chart.js", "WebSocket", "REST"],
repoUrl: "#",
},
{
id: "5",
title: "Mobile Banking App",
description:
"Cross-platform mobile banking application with biometric authentication, push notifications, and offline transaction history.",
category: "mobile",
tags: ["React Native", "TypeScript", "Redux"],
},
{
id: "6",
title: "Authentication Microservice",
description:
"Centralized auth service with OAuth2, JWT, MFA, and session management. Supports SSO across 8 internal applications.",
category: "backend",
tags: ["Spring Security", "OAuth2", "JWT", "Redis"],
metrics: "Securing 50K+ active users",
},
];
const filters = [
{ value: "all", label: "All Projects", icon: <Layers size={16} /> },
{ value: "backend", label: "Backend", icon: <Server size={16} /> },
{ value: "frontend", label: "Frontend", icon: <Globe size={16} /> },
{ value: "mobile", label: "Mobile", icon: <Smartphone size={16} /> },
];
export function ProjectsSection() {
const [activeFilter, setActiveFilter] = useState("all");
const filteredProjects =
activeFilter === "all"
? projects
: projects.filter((p) => p.category === activeFilter);
return (
<section id="projects" className="section-padding relative">
<div className="max-w-6xl mx-auto px-6">
<AnimatedSection>
<SectionHeading
badge="Portfolio"
title="Projects & Case Studies"
subtitle="Real-world enterprise solutions built for scale, security, and reliability."
/>
</AnimatedSection>
{/* Filter tabs */}
<AnimatedSection delay={0.1}>
<div className="flex flex-wrap items-center justify-center gap-2 mb-12">
{filters.map((filter) => (
<button
key={filter.value}
onClick={() => setActiveFilter(filter.value)}
className={`inline-flex items-center gap-2 px-4 py-2.5 rounded-xl text-sm font-medium transition-all duration-300 ${activeFilter === filter.value
? "bg-accent text-accent-foreground shadow-lg shadow-accent/25"
: "bg-muted/50 text-muted-foreground border border-border/50 hover:border-accent/30 hover:text-foreground"
}`}
>
{filter.icon}
{filter.label}
</button>
))}
</div>
</AnimatedSection>
{/* Projects grid */}
<motion.div
layout
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5"
>
<AnimatePresence mode="popLayout">
{filteredProjects.map((project) => (
<motion.div
key={project.id}
layout
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
transition={{ duration: 0.3 }}
>
<div className="group relative h-full flex flex-col p-6 rounded-2xl bg-card border border-border/50 hover:border-accent/30 transition-all duration-500 hover:shadow-lg hover:shadow-accent/5">
{/* Category badge */}
<div className="flex items-center justify-between mb-4">
<span
className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-[10px] font-mono font-semibold uppercase tracking-wider ${project.category === "backend"
? "bg-blue-500/10 text-blue-500 dark:text-blue-400"
: project.category === "frontend"
? "bg-violet-500/10 text-violet-500 dark:text-violet-400"
: "bg-orange-500/10 text-orange-500 dark:text-orange-400"
}`}
>
{project.category === "backend" ? (
<Server size={12} />
) : project.category === "frontend" ? (
<Globe size={12} />
) : (
<Smartphone size={12} />
)}
{project.category}
</span>
<div className="flex items-center gap-1.5">
{project.repoUrl && (
<a
href={project.repoUrl}
className="p-1.5 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors"
aria-label="GitHub"
>
<GitFork size={14} />
</a>
)}
{project.liveUrl && (
<a
href={project.liveUrl}
className="p-1.5 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors"
aria-label="Live demo"
>
<ExternalLink size={14} />
</a>
)}
</div>
</div>
{/* Title & description */}
<h3 className="text-lg font-bold mb-2 group-hover:text-accent transition-colors">
{project.title}
</h3>
<p className="text-sm text-muted-foreground leading-relaxed mb-4 flex-1">
{project.description}
</p>
{/* Metrics badge */}
{project.metrics && (
<div className="flex items-center gap-2 mb-4 px-3 py-2 rounded-lg bg-success/5 border border-success/10">
<TrendingUp size={14} className="text-success" />
<span className="text-xs font-mono font-medium text-success">
{project.metrics}
</span>
</div>
)}
{/* Tags */}
<div className="flex flex-wrap gap-1.5 mt-auto">
{project.tags.map((tag) => (
<span
key={tag}
className="px-2 py-1 rounded-md text-[10px] font-mono bg-muted/50 text-muted-foreground border border-border/30"
>
{tag}
</span>
))}
</div>
</div>
</motion.div>
))}
</AnimatePresence>
</motion.div>
</div>
</section>
);
}