252 lines
8.9 KiB
TypeScript
252 lines
8.9 KiB
TypeScript
"use client";
|
|
|
|
import { useRef, useState, useEffect } from "react";
|
|
import { motion, useInView } from "framer-motion";
|
|
import {
|
|
Layers,
|
|
ShieldCheck,
|
|
Zap,
|
|
RefreshCcw,
|
|
GitBranch,
|
|
Box,
|
|
} from "lucide-react";
|
|
import { useTheme } from "next-themes";
|
|
import SyntaxHighlighter from "react-syntax-highlighter";
|
|
import {
|
|
atomOneDark,
|
|
atomOneLight,
|
|
} from "react-syntax-highlighter/dist/esm/styles/hljs";
|
|
|
|
const principles = [
|
|
{
|
|
icon: Layers,
|
|
title: "Clean Architecture Mindset",
|
|
description:
|
|
"I structure applications with clear separation of concerns so business logic stays maintainable and easy to evolve as the project grows.",
|
|
tag: "Architecture",
|
|
},
|
|
{
|
|
icon: Box,
|
|
title: "Modular Code Structure",
|
|
description:
|
|
"I organize features into well-defined modules to keep the codebase scalable and easier for teams to understand and maintain.",
|
|
tag: "Scalability",
|
|
},
|
|
{
|
|
icon: GitBranch,
|
|
title: "Abstraction & Reusability",
|
|
description:
|
|
"I prefer abstractions and reusable patterns to keep the codebase flexible and easier to extend in future iterations.",
|
|
tag: "Code Design",
|
|
},
|
|
{
|
|
icon: ShieldCheck,
|
|
title: "SOLID Principles",
|
|
description:
|
|
"I apply SOLID principles to write clean, understandable code where each component has a clear responsibility.",
|
|
tag: "Code Quality",
|
|
},
|
|
{
|
|
icon: Zap,
|
|
title: "Performance Awareness",
|
|
description:
|
|
"I consider performance early by applying efficient queries, pagination, and thoughtful data handling in web applications.",
|
|
tag: "Performance",
|
|
},
|
|
{
|
|
icon: RefreshCcw,
|
|
title: "Maintainable Development",
|
|
description:
|
|
"I focus on writing maintainable code, using version control effectively, and building systems that teams can work on confidently.",
|
|
tag: "Workflow",
|
|
},
|
|
];
|
|
|
|
const containerVariants = {
|
|
hidden: { opacity: 0 },
|
|
visible: {
|
|
opacity: 1,
|
|
transition: { staggerChildren: 0.08, delayChildren: 0.1 },
|
|
},
|
|
};
|
|
|
|
const itemVariants = {
|
|
hidden: { opacity: 0, y: 20 },
|
|
visible: { opacity: 1, y: 0, transition: { duration: 0.5, ease: "easeOut" } },
|
|
};
|
|
|
|
export function PrincipleSection() {
|
|
const ref = useRef(null);
|
|
const isInView = useInView(ref, { once: true, margin: "-80px" });
|
|
const { resolvedTheme } = useTheme();
|
|
const [mounted, setMounted] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setMounted(true);
|
|
}, []);
|
|
|
|
const isDark = resolvedTheme === "dark";
|
|
|
|
const codeSnippet = `// ✅ Application Layer — Pure Business Logic
|
|
@Injectable()
|
|
export class UserService {
|
|
constructor(
|
|
private readonly userRepository: IUserRepository, // Interface, not implementation
|
|
private readonly eventEmitter: IEventEmitter,
|
|
) {}
|
|
|
|
async createUser(dto: CreateUserDto): Promise<UserEntity> {
|
|
const exists = await this.userRepository.findByEmail(dto.email)
|
|
if (exists) throw new ConflictException('Email already registered')
|
|
|
|
const user = UserEntity.create(dto) // Domain entity handles business rules
|
|
await this.userRepository.save(user)
|
|
await this.eventEmitter.emit('user.created', user)
|
|
|
|
return user
|
|
}
|
|
}
|
|
|
|
// ✅ Infrastructure Layer — Implementation Detail
|
|
@Injectable()
|
|
export class PrismaUserRepository implements IUserRepository {
|
|
constructor(private prisma: PrismaService) {}
|
|
async findByEmail(email: string) { /* ... */ }
|
|
async save(user: UserEntity) { /* ... */ }
|
|
}`;
|
|
|
|
return (
|
|
<section id="principles" className="py-16 sm:py-20 lg:py-32 relative overflow-hidden">
|
|
{/* Background */}
|
|
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-surface/30 to-transparent" />
|
|
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[800px] h-[400px] bg-accent/4 rounded-full blur-[150px] pointer-events-none" />
|
|
|
|
<div className="relative z-10 max-w-6xl mx-auto px-6">
|
|
{/* Header */}
|
|
<div className="text-center mb-12 sm:mb-16">
|
|
<motion.span
|
|
initial={{ opacity: 0 }}
|
|
whileInView={{ opacity: 1 }}
|
|
viewport={{ once: true }}
|
|
className="text-accent font-mono text-xs sm:text-sm tracking-widest uppercase"
|
|
>
|
|
System Design
|
|
</motion.span>
|
|
<motion.h2
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.5, delay: 0.1 }}
|
|
className="font-display font-bold text-3xl sm:text-4xl md:text-5xl text-foreground mt-3"
|
|
>
|
|
Engineering
|
|
<span className="gradient-text-blue"> Principles</span>
|
|
</motion.h2>
|
|
<motion.p
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.5, delay: 0.2 }}
|
|
className="text-foreground-muted mt-4 max-w-2xl mx-auto leading-relaxed text-sm sm:text-base px-4"
|
|
>
|
|
I aim to build systems that are clean, maintainable, and ready to grow as products evolve.
|
|
These principles guide how I structure applications and approach technical decisions.
|
|
</motion.p>
|
|
</div>
|
|
|
|
{/* Principles Grid */}
|
|
<motion.div
|
|
ref={ref}
|
|
variants={containerVariants}
|
|
initial="hidden"
|
|
animate={isInView ? "visible" : "hidden"}
|
|
className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 sm:gap-5"
|
|
>
|
|
{principles.map((p) => {
|
|
const Icon = p.icon;
|
|
return (
|
|
<motion.div
|
|
key={p.title}
|
|
variants={itemVariants}
|
|
className="group p-4 sm:p-6 rounded-2xl bg-surface-elevated border border-border hover:border-accent/30 hover:shadow-glow transition-all duration-300"
|
|
>
|
|
<div className="flex items-start justify-between gap-4 mb-4">
|
|
<div className="w-10 h-10 rounded-xl bg-accent/10 border border-border flex items-center justify-center group-hover:bg-accent/20 transition-colors">
|
|
<Icon size={18} className="text-accent" />
|
|
</div>
|
|
<span className="text-xs font-mono text-foreground-subtle bg-surface border border-border-subtle px-2.5 py-1 rounded-lg">
|
|
{p.tag}
|
|
</span>
|
|
</div>
|
|
<h3 className="font-display font-semibold text-foreground text-sm sm:text-base mb-3 leading-snug">
|
|
{p.title}
|
|
</h3>
|
|
<p className="text-xs sm:text-sm text-foreground-muted leading-relaxed">
|
|
{p.description}
|
|
</p>
|
|
</motion.div>
|
|
);
|
|
})}
|
|
</motion.div>
|
|
|
|
{/* Code snippet preview */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true }}
|
|
transition={{ duration: 0.6, delay: 0.3 }}
|
|
className="mt-8 sm:mt-10 rounded-2xl bg-surface-elevated border border-border overflow-hidden"
|
|
>
|
|
{/* macOS-style title bar */}
|
|
<div className="flex items-center gap-2 sm:gap-3 px-3 sm:px-5 py-3 sm:py-3.5 border-b border-border bg-surface">
|
|
<div className="flex gap-1.5">
|
|
<div className="w-3 h-3 rounded-full bg-red-500/70" />
|
|
<div className="w-3 h-3 rounded-full bg-yellow-500/70" />
|
|
<div className="w-3 h-3 rounded-full bg-green-500/70" />
|
|
</div>
|
|
<span className="text-xs font-mono text-foreground-muted ml-1 truncate">
|
|
user.service.ts
|
|
</span>
|
|
<span className="hidden sm:inline ml-auto text-xs font-mono text-foreground-subtle opacity-60">
|
|
Clean Architecture Example
|
|
</span>
|
|
</div>
|
|
|
|
{/* Syntax-highlighted code */}
|
|
{mounted ? (
|
|
<SyntaxHighlighter
|
|
language="typescript"
|
|
style={isDark ? atomOneDark : atomOneLight}
|
|
showLineNumbers={false}
|
|
wrapLongLines={true}
|
|
customStyle={{
|
|
margin: 0,
|
|
padding: "1rem 1.5rem",
|
|
fontSize: "0.6875rem",
|
|
lineHeight: "1.6",
|
|
background: "transparent",
|
|
overflowX: "auto",
|
|
}}
|
|
lineNumberStyle={{
|
|
color: isDark ? "#3d4451" : "#c4c9d4",
|
|
paddingRight: "1.5rem",
|
|
minWidth: "2.5rem",
|
|
userSelect: "none",
|
|
}}
|
|
>
|
|
{codeSnippet}
|
|
</SyntaxHighlighter>
|
|
) : (
|
|
<div
|
|
className="p-6 overflow-x-auto text-[0.75rem] font-mono leading-[1.7] opacity-0 text-transparent"
|
|
aria-hidden="true"
|
|
>
|
|
<pre>{codeSnippet}</pre>
|
|
</div>
|
|
)}
|
|
</motion.div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|