Files
Portfolio/src/components-vite/SkillsMatrix.tsx
T
damjan_savicandClaude Opus 4.5 b1ec7b4d61 Migrate from Vite to Next.js 15 with SSR
- Replace Vite + React Router with Next.js 15 App Router
- Implement i18n with next-intl (URL-based: /de, /en, /sr)
- Add SSR/SSG for all pages (48 static pages generated)
- Setup Supabase SSR client for auth
- Migrate all pages: Home, About, Portfolio, Blog, Contact, Login, Dashboard, Imprint, Privacy, Terms
- Add Docker support with standalone output
- Replace i18next with next-intl JSON translations
- Use next/image for optimized images

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 01:00:33 +01:00

60 lines
2.1 KiB
TypeScript

import React from 'react';
import { motion } from 'framer-motion';
import { useInView } from 'react-intersection-observer';
interface Skill {
name: string;
level: number;
category: string;
description: string;
}
interface SkillsMatrixProps {
skills: Skill[];
}
const SkillsMatrix: React.FC<SkillsMatrixProps> = ({ skills }) => {
const [ref, inView] = useInView({
triggerOnce: true,
threshold: 0.1,
});
const categories = Array.from(new Set(skills.map(skill => skill.category)));
return (
<div ref={ref} className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{categories.map((category, categoryIndex) => (
<div key={category} className="bg-gray-900/50 p-6 rounded-xl">
<h3 className="text-xl font-semibold mb-6">{category}</h3>
<div className="space-y-6">
{skills
.filter(skill => skill.category === category)
.map((skill, skillIndex) => (
<div key={skill.name}>
<div className="flex justify-between mb-2">
<div className="group relative">
<span>{skill.name}</span>
<div className="absolute bottom-full left-0 mb-2 w-64 p-4 bg-gray-800 rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200">
<p className="text-sm text-white/80">{skill.description}</p>
</div>
</div>
<span>{skill.level}%</span>
</div>
<div className="h-2 bg-gray-700 rounded-full overflow-hidden">
<motion.div
className="h-full bg-orange-500"
initial={{ width: 0 }}
animate={inView ? { width: `${skill.level}%` } : { width: 0 }}
transition={{ duration: 1, delay: categoryIndex * 0.2 + skillIndex * 0.1 }}
/>
</div>
</div>
))}
</div>
</div>
))}
</div>
);
};
export default SkillsMatrix;