vkorobtsev-nextJs/app/about/components/skills/skills.jsx
2025-05-26 22:09:21 +07:00

249 lines
6.8 KiB
JavaScript

"use client";
import { motion, AnimatePresence } from "framer-motion";
import { useState } from "react";
import { CodepenIcon, WebhookIcon, ActivityIcon, MobileIcon } from "./icons"
const skillCategories = {
web: {
title: "Web Development",
icon: CodepenIcon,
description: "Building modern, responsive web applications",
languages: [
"HTML",
"CSS",
"JavaScript",
"TypeScript",
"PHP",
"Python",
"React",
"NextJS",
"TailwindCSS",
"Bootstrap",
"NodeJS",
"ExpressJS",
"Laravel",
"Flask",
"Django",
"Firebase"
],
tools: ["Visual Studio Code", "Git", "Github", "Figma", "Vite", "Docker", "Kubernetes", "Google Cloud", "Postman"],
},
api: {
title: "REST API",
icon: WebhookIcon,
description: "Creating robust and scalable backend services",
languages: [
"NodeJS",
"ExpressJS",
"PHP",
"Laravel",
"Python",
"FastAPI",
"Flask",
"Django",
"MySQL",
"PostgreSQL",
"MongoDB",
"Firebase",
],
tools: ["Postman", "Docker", "Kubernetes", "Swagger", "Git", "Github", "Google Cloud", "IBM Cloud"],
},
ai: {
title: "AI & Machine Learning",
icon: ActivityIcon,
description: "Developing intelligent solutions with ML/AI",
languages: [
"Python",
"TensorFlow",
"PyTorch",
"Scikit-learn",
"Pandas",
"NumPy",
"Jupyter",
"OpenAI API",
"Gemini API",
"LangChain",
],
tools: [
"Jupyter Notebook",
"Google Colab",
"Google Cloud AI",
"AWS SageMaker",
"IBM Watson",
],
},
mobile: {
title: "Mobile Development",
icon: MobileIcon,
description: "Cross-platform mobile app development",
languages: [
"React Native",
"JavaScript",
"TypeScript",
"Dart",
"Flutter",
],
tools: [
"Android Studio",
"React Native CLI",
],
},
};
function SkillCard({ skill, isSelected, onClick }) {
const Icon = skill.icon;
return (
<motion.div
onClick={onClick}
className={`relative cursor-pointer group p-6 rounded-2xl backdrop-blur-lg border transition-all duration-300 ${
isSelected
? "bg-white/20 border-black border-2 shadow-lg"
: "bg-white/10 border-gray-300/20 hover:bg-white/20 hover:border-gray-300/30"
}`}
whileHover={{
scale: 1.05,
rotateY: 5,
}}
whileTap={{ scale: 0.95 }}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
type: "spring",
stiffness: 300,
damping: 20,
}}>
{/* Glow effect - removed for selected state */}
{!isSelected && (
<div className="absolute inset-0 rounded-2xl transition-opacity duration-300 opacity-0 group-hover:opacity-50 bg-gradient-to-r from-gray-400/20 to-gray-600/20 blur-xl" />
)}
<div className="relative z-10 flex flex-col items-center text-center space-y-4">
<div
className={`p-4 rounded-xl transition-all duration-300 ${
isSelected ? "bg-white/30" : "bg-white/10 group-hover:bg-white/20"
}`}>
<Icon className="w-8 h-8 text-black" />
</div>
<div>
<h3 className="font-semibold text-black text-lg mb-2">
{skill.title}
</h3>
<p className="text-gray-600 text-sm leading-relaxed">
{skill.description}
</p>
</div>
</div>
</motion.div>
);
}
function SkillDetails({ selectedSkill }) {
if (!selectedSkill) return null;
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.5 }}
className="mt-12 space-y-8">
{/* Languages & Frameworks */}
<motion.div
className="backdrop-blur-lg bg-white/20 border border-gray-300/30 rounded-2xl p-8"
initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.2 }}>
<h3 className="text-2xl font-semibold text-black mb-6 text-center">
Languages & Frameworks
</h3>
<div className="flex flex-wrap justify-center gap-3">
{selectedSkill.languages.map((lang, index) => (
<motion.span
key={lang}
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.3 + index * 0.1 }}
className="px-4 py-2 bg-gradient-to-r from-gray-200/60 to-white/40
border border-gray-400/40 rounded-full text-black font-medium
backdrop-blur-sm hover:scale-105 transition-transform cursor-default
hover:bg-gradient-to-r hover:from-gray-300/60 hover:to-white/50">
{lang}
</motion.span>
))}
</div>
</motion.div>
{/* Tools */}
<motion.div
className="backdrop-blur-lg bg-white/20 border border-gray-300/30 rounded-2xl p-8"
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: 0.4 }}>
<h3 className="text-2xl font-semibold text-black mb-6 text-center">
Tools & Technologies
</h3>
<div className="flex flex-wrap justify-center gap-3">
{selectedSkill.tools.map((tool, index) => (
<motion.span
key={tool}
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: 0.5 + index * 0.1 }}
className="px-4 py-2 bg-gradient-to-r from-gray-300/60 to-gray-100/40
border border-gray-500/40 rounded-full text-black font-medium
backdrop-blur-sm hover:scale-105 transition-transform cursor-default
hover:bg-gradient-to-r hover:from-gray-400/60 hover:to-gray-200/50">
{tool}
</motion.span>
))}
</div>
</motion.div>
</motion.div>
);
}
export default function Skills() {
const [selectedCategory, setSelectedCategory] = useState("web");
return (
<div className="relative">
<div className="mx-auto container px-6 py-20">
<motion.div
initial={{ opacity: 0, y: -20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="text-center space-y-4 mb-16">
<h2 className="text-5xl font-bold bg-gradient-to-r from-black to-gray-600 bg-clip-text text-transparent">
Skills & Expertise
</h2>
<p className="text-gray-600 max-w-2xl mx-auto text-lg leading-relaxed">
Explore my technical skills across different domains. Click on any
category to see the specific technologies and tools I work with.
</p>
</motion.div>
{/* Skill Categories Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{Object.entries(skillCategories).map(([key, skill], index) => (
<motion.div
key={key}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}>
<SkillCard
skill={skill}
isSelected={selectedCategory === key}
onClick={() => setSelectedCategory(key)}
/>
</motion.div>
))}
</div>
{/* Skill Details */}
<AnimatePresence mode="wait">
<SkillDetails selectedSkill={skillCategories[selectedCategory]} />
</AnimatePresence>
</div>
</div>
);
}