first commit

This commit is contained in:
Dayron
2025-11-11 17:12:59 +01:00
commit 760849a5c2
24 changed files with 7124 additions and 0 deletions

140
src/components/About.tsx Normal file
View File

@@ -0,0 +1,140 @@
import { motion } from 'framer-motion';
import { User, Heart, Target, Coffee } from 'lucide-react';
const About = () => {
const stats = [
{ icon: <User size={24} />, value: "3ème", label: "Année d'études" },
{ icon: <Heart size={24} />, value: "100%", label: "Passion" },
{ icon: <Target size={24} />, value: "∞", label: "Objectifs" },
{ icon: <Coffee size={24} />, value: "☕", label: "Fuel quotidien" }
];
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
delayChildren: 0.3
}
}
};
const itemVariants = {
hidden: { opacity: 0, y: 50 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.6,
ease: [0.25, 0.1, 0.25, 1] as const
}
}
};
return (
<section id="about" className="about">
<div className="container">
<motion.div
className="section-header"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
viewport={{ once: true }}
>
<h2 className="section-title">À propos de moi</h2>
<p className="section-subtitle">
Découvrez qui je suis et ce qui me passionne
</p>
</motion.div>
<div className="about-content">
<motion.div
className="about-text"
variants={containerVariants}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
<motion.div className="about-card" variants={itemVariants}>
<h3>Mon parcours</h3>
<p>
Actuellement en 3ème année de <strong>Technologies de l'Informatique</strong> à la
HELHa de Tournai, je me passionne pour le développement d'applications et les
nouvelles technologies. Mon parcours m'a permis d'acquérir une solide base
technique et une approche méthodique du développement.
</p>
</motion.div>
<motion.div className="about-card" variants={itemVariants}>
<h3>Ma passion</h3>
<p>
Ce qui m'anime le plus, c'est la création d'solutions innovantes qui résolvent
des problèmes réels. J'aime particulièrement le développement mobile avec
<strong> Flutter</strong> et le développement web moderne avec
<strong> React</strong> et <strong> TypeScript</strong>.
</p>
</motion.div>
<motion.div className="about-card" variants={itemVariants}>
<h3>Mes objectifs</h3>
<p>
Je cherche constamment à améliorer mes compétences et à rester à jour avec
les dernières tendances technologiques. Mon objectif est de devenir un
développeur full-stack polyvalent et de contribuer à des projets qui ont
un impact positif.
</p>
</motion.div>
</motion.div>
<motion.div
className="about-stats"
variants={containerVariants}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
{stats.map((stat, index) => (
<motion.div
key={index}
className="stat-card"
variants={itemVariants}
whileHover={{
scale: 1.05,
y: -5,
transition: { duration: 0.3 }
}}
>
<div className="stat-icon">
{stat.icon}
</div>
<div className="stat-value">{stat.value}</div>
<div className="stat-label">{stat.label}</div>
</motion.div>
))}
</motion.div>
</div>
<motion.div
className="about-highlight"
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.8, delay: 0.5 }}
viewport={{ once: true }}
>
<div className="highlight-content">
<h3>En quelques mots</h3>
<p>
"La technologie n'est rien. Ce qui est important, c'est d'avoir la foi en les gens,
qu'ils soient fondamentalement bons et intelligents, et si vous leur donnez des outils,
ils feront des choses merveilleuses avec."
</p>
<cite>- Steve Jobs</cite>
</div>
</motion.div>
</div>
</section>
);
};
export default About;

341
src/components/Contact.tsx Normal file
View File

@@ -0,0 +1,341 @@
import { useState } from 'react';
import { motion } from 'framer-motion';
import { Mail, Phone, MapPin, Send, Github, Linkedin, MessageCircle, CheckCircle } from 'lucide-react';
const Contact = () => {
const [formData, setFormData] = useState({
name: '',
email: '',
subject: '',
message: ''
});
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSubmitted, setIsSubmitted] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
// Simulation d'envoi (remplacez par votre logique d'envoi réelle)
setTimeout(() => {
setIsSubmitting(false);
setIsSubmitted(true);
setFormData({ name: '', email: '', subject: '', message: '' });
// Reset du message de succès après 5 secondes
setTimeout(() => {
setIsSubmitted(false);
}, 5000);
}, 2000);
};
const contactInfo = [
{
icon: <Mail size={24} />,
title: "Email",
content: "dayron.vanleemput@example.com", // Remplacez par votre email
link: "mailto:dayron.vanleemput@example.com",
color: "#EA4335"
},
{
icon: <Phone size={24} />,
title: "Téléphone",
content: "+32 XXX XX XX XX", // Remplacez par votre numéro
link: "tel:+32XXXXXXXXX",
color: "#34A853"
},
{
icon: <MapPin size={24} />,
title: "Localisation",
content: "Tournai, Belgique",
link: "https://maps.google.com/?q=Tournai,Belgium",
color: "#4285F4"
}
];
const socialLinks = [
{
icon: <Github size={24} />,
name: "GitHub",
url: "https://github.com/dayronvanleemput", // Remplacez par votre profil
color: "#333"
},
{
icon: <Linkedin size={24} />,
name: "LinkedIn",
url: "https://linkedin.com/in/dayronvanleemput", // Remplacez par votre profil
color: "#0077B5"
}
];
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
delayChildren: 0.3
}
}
};
const itemVariants = {
hidden: { opacity: 0, y: 50 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.6,
ease: [0.25, 0.1, 0.25, 1] as const
}
}
};
return (
<section id="contact" className="contact">
<div className="container">
<motion.div
className="section-header"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
viewport={{ once: true }}
>
<h2 className="section-title">Contactez-moi</h2>
<p className="section-subtitle">
Une question, un projet ou simplement envie d'échanger ? N'hésitez pas à me contacter !
</p>
</motion.div>
<motion.div
className="contact-content"
variants={containerVariants}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
{/* Informations de contact */}
<motion.div className="contact-info" variants={itemVariants}>
<div className="contact-intro">
<h3>
<MessageCircle size={24} />
Restons en contact
</h3>
<p>
Je suis toujours intéressé par de nouveaux projets, des collaborations
ou simplement des discussions autour de la technologie. N'hésitez pas
à me contacter !
</p>
</div>
<div className="contact-methods">
{contactInfo.map((contact, index) => (
<motion.a
key={index}
href={contact.link}
className="contact-method"
initial={{ opacity: 0, x: -30 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
whileHover={{
scale: 1.05,
x: 10,
transition: { duration: 0.2 }
}}
viewport={{ once: true }}
>
<div
className="contact-icon"
style={{ backgroundColor: `${contact.color}20`, color: contact.color }}
>
{contact.icon}
</div>
<div className="contact-details">
<h4>{contact.title}</h4>
<span>{contact.content}</span>
</div>
</motion.a>
))}
</div>
<div className="social-links">
<h4>Retrouvez-moi aussi sur :</h4>
<div className="social-grid">
{socialLinks.map((social, index) => (
<motion.a
key={index}
href={social.url}
target="_blank"
rel="noopener noreferrer"
className="social-link"
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
whileHover={{
scale: 1.1,
rotate: 5,
transition: { duration: 0.2 }
}}
viewport={{ once: true }}
>
<div
className="social-icon"
style={{ backgroundColor: `${social.color}20`, color: social.color }}
>
{social.icon}
</div>
<span>{social.name}</span>
</motion.a>
))}
</div>
</div>
</motion.div>
{/* Formulaire de contact */}
<motion.div className="contact-form-container" variants={itemVariants}>
<h3>Envoyez-moi un message</h3>
{isSubmitted && (
<motion.div
className="success-message"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
>
<CheckCircle size={20} />
Message envoyé avec succès ! Je vous répondrai bientôt.
</motion.div>
)}
<form onSubmit={handleSubmit} className="contact-form">
<div className="form-row">
<motion.div
className="form-group"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.1 }}
viewport={{ once: true }}
>
<label htmlFor="name">Nom complet</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
required
placeholder="Votre nom"
/>
</motion.div>
<motion.div
className="form-group"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
viewport={{ once: true }}
>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
name="email"
value={formData.email}
onChange={handleChange}
required
placeholder="votre.email@example.com"
/>
</motion.div>
</div>
<motion.div
className="form-group"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.3 }}
viewport={{ once: true }}
>
<label htmlFor="subject">Sujet</label>
<input
type="text"
id="subject"
name="subject"
value={formData.subject}
onChange={handleChange}
required
placeholder="Objet de votre message"
/>
</motion.div>
<motion.div
className="form-group"
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.4 }}
viewport={{ once: true }}
>
<label htmlFor="message">Message</label>
<textarea
id="message"
name="message"
value={formData.message}
onChange={handleChange}
required
rows={6}
placeholder="Votre message..."
/>
</motion.div>
<motion.button
type="submit"
className={`submit-btn ${isSubmitting ? 'submitting' : ''}`}
disabled={isSubmitting}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.5 }}
whileHover={!isSubmitting ? { scale: 1.05 } : {}}
whileTap={!isSubmitting ? { scale: 0.95 } : {}}
viewport={{ once: true }}
>
{isSubmitting ? (
<>
<div className="loading-spinner" />
Envoi en cours...
</>
) : (
<>
<Send size={20} />
Envoyer le message
</>
)}
</motion.button>
</form>
</motion.div>
</motion.div>
{/* Footer */}
<motion.footer
className="contact-footer"
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
transition={{ duration: 0.8, delay: 0.5 }}
viewport={{ once: true }}
>
<p>
© 2025 Dayron Van Leemput. Développé avec ❤️ en React et TypeScript.
</p>
</motion.footer>
</div>
</section>
);
};
export default Contact;

View File

@@ -0,0 +1,270 @@
import { motion } from 'framer-motion';
import { GraduationCap, Calendar, MapPin, Award, BookOpen, Target } from 'lucide-react';
const Education = () => {
const education = [
{
id: 1,
degree: "Bachelier en Technologies de l'Informatique",
school: "HELHa - Haute École Louvain en Hainaut",
location: "Tournai, Belgique",
period: "2023 - 2026",
currentYear: "3ème année",
status: "En cours",
description: "Formation complète en développement logiciel, programmation, bases de données, réseaux et gestion de projets informatiques.",
highlights: [
"Programmation orientée objet (Java, C#)",
"Développement web (HTML, CSS, JavaScript, React)",
"Développement mobile (Flutter, Dart)",
"Bases de données et SQL",
"Gestion de projets",
"Réseaux et systèmes"
],
color: "#4CAF50",
icon: <GraduationCap size={24} />
}
];
const certifications = [
{
title: "Développement Mobile Flutter",
provider: "Formation autodidacte",
date: "2024",
skills: ["Dart", "Flutter", "Firebase", "API REST"],
color: "#2196F3"
},
{
title: "React & TypeScript",
provider: "Projets personnels",
date: "2024",
skills: ["React", "TypeScript", "Hooks", "Context API"],
color: "#FF9800"
}
];
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
delayChildren: 0.3
}
}
};
const itemVariants = {
hidden: { opacity: 0, x: -50 },
visible: {
opacity: 1,
x: 0,
transition: {
duration: 0.6,
ease: [0.25, 0.1, 0.25, 1] as const
}
}
};
return (
<section id="education" className="education">
<div className="container">
<motion.div
className="section-header"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
viewport={{ once: true }}
>
<h2 className="section-title">Formation & Apprentissage</h2>
<p className="section-subtitle">
Mon parcours académique et mes apprentissages continus
</p>
</motion.div>
<div className="education-content">
{/* Formation principale */}
<motion.div
className="education-main"
variants={containerVariants}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
{education.map((edu) => (
<motion.div
key={edu.id}
className="education-card main-education"
variants={itemVariants}
whileHover={{
scale: 1.02,
y: -5,
transition: { duration: 0.3 }
}}
>
<div className="education-timeline">
<div
className="timeline-dot"
style={{ backgroundColor: edu.color }}
>
{edu.icon}
</div>
<div className="timeline-line"></div>
</div>
<div className="education-content-card">
<div className="education-header">
<div className="education-title">
<h3>{edu.degree}</h3>
<div className="education-meta">
<span className="school-name">
<BookOpen size={16} />
{edu.school}
</span>
<span className="location">
<MapPin size={16} />
{edu.location}
</span>
<span className="period">
<Calendar size={16} />
{edu.period}
</span>
</div>
</div>
<div className="education-status">
<span
className="status-badge"
style={{ backgroundColor: `${edu.color}20`, color: edu.color }}
>
{edu.status}
</span>
<span className="current-year">{edu.currentYear}</span>
</div>
</div>
<p className="education-description">{edu.description}</p>
<div className="education-highlights">
<h4>
<Target size={16} />
Matières principales
</h4>
<div className="highlights-grid">
{edu.highlights.map((highlight, index) => (
<motion.span
key={index}
className="highlight-tag"
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3, delay: index * 0.1 }}
viewport={{ once: true }}
>
{highlight}
</motion.span>
))}
</div>
</div>
</div>
</motion.div>
))}
</motion.div>
{/* Certifications et formations complémentaires */}
<motion.div
className="certifications"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.3 }}
viewport={{ once: true }}
>
<h3 className="certifications-title">
<Award size={24} />
Formations complémentaires & Autodidacte
</h3>
<div className="certifications-grid">
{certifications.map((cert, index) => (
<motion.div
key={index}
className="certification-card"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
whileHover={{
scale: 1.03,
y: -3,
transition: { duration: 0.2 }
}}
viewport={{ once: true }}
>
<div className="cert-header">
<h4>{cert.title}</h4>
<span className="cert-provider">{cert.provider}</span>
<span className="cert-date">{cert.date}</span>
</div>
<div className="cert-skills">
{cert.skills.map((skill, skillIndex) => (
<span
key={skillIndex}
className="cert-skill-tag"
style={{ backgroundColor: `${cert.color}20`, color: cert.color }}
>
{skill}
</span>
))}
</div>
</motion.div>
))}
</div>
</motion.div>
{/* Objectifs d'apprentissage */}
<motion.div
className="learning-goals"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.4 }}
viewport={{ once: true }}
>
<h3>Objectifs d'apprentissage 2025</h3>
<div className="goals-grid">
{[
{ goal: "Maîtriser Firebase et les services cloud", progress: 60 },
{ goal: "Approfondir Spring Boot pour le backend", progress: 30 },
{ goal: "Apprendre Docker et les conteneurs", progress: 20 },
{ goal: "Développer mes compétences en UI/UX", progress: 45 }
].map((item, index) => (
<motion.div
key={index}
className="goal-item"
initial={{ opacity: 0, x: -30 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
viewport={{ once: true }}
>
<div className="goal-header">
<span className="goal-text">{item.goal}</span>
<span className="goal-percentage">{item.progress}%</span>
</div>
<div className="goal-progress">
<motion.div
className="goal-progress-bar"
initial={{ width: 0 }}
whileInView={{ width: `${item.progress}%` }}
transition={{
duration: 1.5,
delay: index * 0.1 + 0.5,
ease: [0.25, 0.1, 0.25, 1] as const
}}
viewport={{ once: true }}
/>
</div>
</motion.div>
))}
</div>
</motion.div>
</div>
</div>
</section>
);
};
export default Education;

120
src/components/Header.tsx Normal file
View File

@@ -0,0 +1,120 @@
import { useState } from 'react';
import { motion } from 'framer-motion';
import { Menu, X, Sun, Moon } from 'lucide-react';
interface HeaderProps {
darkMode: boolean;
toggleDarkMode: () => void;
}
const Header = ({ darkMode, toggleDarkMode }: HeaderProps) => {
const [isMenuOpen, setIsMenuOpen] = useState(false);
const menuItems = [
{ name: 'Accueil', href: '#hero' },
{ name: 'À propos', href: '#about' },
{ name: 'Compétences', href: '#skills' },
{ name: 'Projets', href: '#projects' },
{ name: 'Formation', href: '#education' },
{ name: 'Contact', href: '#contact' }
];
const scrollToSection = (href: string) => {
const element = document.querySelector(href);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
setIsMenuOpen(false);
};
return (
<motion.header
initial={{ y: -100 }}
animate={{ y: 0 }}
transition={{ duration: 0.5 }}
className="header"
>
<nav className="nav">
<motion.div
className="nav-brand"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<a href="#hero" onClick={(e) => { e.preventDefault(); scrollToSection('#hero'); }}>
Dayron Van Leemput
</a>
</motion.div>
{/* Navigation desktop */}
<ul className="nav-menu desktop-menu">
{menuItems.map((item, index) => (
<motion.li
key={item.name}
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<a
href={item.href}
onClick={(e) => {
e.preventDefault();
scrollToSection(item.href);
}}
>
{item.name}
</a>
</motion.li>
))}
</ul>
<div className="nav-controls">
{/* Toggle thème */}
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={toggleDarkMode}
className="theme-toggle"
aria-label="Changer de thème"
>
{darkMode ? <Sun size={20} /> : <Moon size={20} />}
</motion.button>
{/* Menu hamburger mobile */}
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={() => setIsMenuOpen(!isMenuOpen)}
className="menu-toggle"
aria-label="Menu"
>
{isMenuOpen ? <X size={24} /> : <Menu size={24} />}
</motion.button>
</div>
{/* Navigation mobile */}
<motion.ul
className={`nav-menu mobile-menu ${isMenuOpen ? 'open' : ''}`}
initial={false}
animate={isMenuOpen ? { opacity: 1, x: 0 } : { opacity: 0, x: '100%' }}
transition={{ duration: 0.3 }}
>
{menuItems.map((item) => (
<li key={item.name}>
<a
href={item.href}
onClick={(e) => {
e.preventDefault();
scrollToSection(item.href);
}}
>
{item.name}
</a>
</li>
))}
</motion.ul>
</nav>
</motion.header>
);
};
export default Header;

159
src/components/Hero.tsx Normal file
View File

@@ -0,0 +1,159 @@
import { motion } from 'framer-motion';
import { Download, Github, Linkedin, Mail } from 'lucide-react';
const Hero = () => {
const handleDownloadCV = () => {
// Ici, vous pouvez ajouter le lien vers votre CV
const link = document.createElement('a');
link.href = '/cv-dayron-van-leemput.pdf'; // Ajoutez votre CV dans le dossier public
link.download = 'CV-Dayron-Van-Leemput.pdf';
link.click();
};
return (
<section id="hero" className="hero">
<div className="hero-content">
<motion.div
className="hero-text"
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.2 }}
>
<motion.h1
className="hero-title"
initial={{ opacity: 0, x: -50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8, delay: 0.4 }}
>
Dayron Van Leemput
</motion.h1>
<motion.h2
className="hero-subtitle"
initial={{ opacity: 0, x: 50 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.8, delay: 0.6 }}
>
Étudiant en Technologies de l'Informatique
</motion.h2>
<motion.p
className="hero-description"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.8 }}
>
Bac 3 à la HELHa de Tournai | Jeune développeur passionné par les nouvelles technologies
et le développement d'applications innovantes
</motion.p>
<motion.div
className="hero-buttons"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 1 }}
>
<motion.button
onClick={handleDownloadCV}
className="btn btn-primary"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<Download size={20} />
Télécharger mon CV
</motion.button>
<motion.a
href="#contact"
onClick={(e) => {
e.preventDefault();
document.querySelector('#contact')?.scrollIntoView({ behavior: 'smooth' });
}}
className="btn btn-secondary"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<Mail size={20} />
Me contacter
</motion.a>
</motion.div>
<motion.div
className="hero-social"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 1.2 }}
>
<motion.a
href="https://github.com/dayronvanleemput" // Remplacez par votre profil GitHub
target="_blank"
rel="noopener noreferrer"
className="social-link"
whileHover={{ scale: 1.2, rotate: 5 }}
whileTap={{ scale: 0.9 }}
>
<Github size={24} />
</motion.a>
<motion.a
href="https://linkedin.com/in/dayronvanleemput" // Remplacez par votre profil LinkedIn
target="_blank"
rel="noopener noreferrer"
className="social-link"
whileHover={{ scale: 1.2, rotate: -5 }}
whileTap={{ scale: 0.9 }}
>
<Linkedin size={24} />
</motion.a>
</motion.div>
</motion.div>
<motion.div
className="hero-image"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 1, delay: 0.5 }}
>
<motion.div
className="hero-avatar"
whileHover={{ scale: 1.05, rotate: 5 }}
transition={{ type: "spring", stiffness: 300, damping: 10 }}
>
{/* Vous pouvez remplacer ceci par votre photo */}
<div className="avatar-placeholder">
<span>DV</span>
</div>
</motion.div>
</motion.div>
</div>
{/* Particules d'animation en arrière-plan */}
<div className="hero-particles">
{[...Array(20)].map((_, i) => (
<motion.div
key={i}
className="particle"
initial={{
opacity: 0,
scale: 0,
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
}}
animate={{
opacity: [0, 1, 0],
scale: [0, 1, 0],
y: [null, -100],
}}
transition={{
duration: Math.random() * 3 + 2,
repeat: Infinity,
delay: Math.random() * 2,
}}
/>
))}
</div>
</section>
);
};
export default Hero;

250
src/components/Projects.tsx Normal file
View File

@@ -0,0 +1,250 @@
import { motion } from 'framer-motion';
import { ExternalLink, Github, Calendar, MapPin } from 'lucide-react';
const Projects = () => {
const projects = [
{
id: 1,
title: "Travel Mate",
description: "Application mobile conçue pour simplifier l'organisation de voyages de groupe. Elle permet de centraliser toutes les informations importantes d'un voyage : planification, gestion des dépenses, découverte d'activités et coordination entre les participants.",
status: "Bientôt disponible sur App Store et Play Store",
technologies: ["Dart", "Flutter", "Firebase"],
features: [
"Planification de voyage collaborative",
"Gestion des dépenses partagées",
"Découverte d'activités locales",
"Coordination en temps réel"
],
color: "#4CAF50",
icon: <MapPin size={24} />,
links: {
github: "#", // Remplacez par votre lien GitHub
demo: "#"
},
image: "/travel-mate-preview.png" // Ajoutez votre image dans le dossier public
},
{
id: 2,
title: "Portfolio Web",
description: "Site web personnel moderne et responsive développé avec React et TypeScript. Inclut des animations fluides, un mode sombre/clair et une architecture modulaire.",
status: "Projet actuel",
technologies: ["React", "TypeScript", "Framer Motion", "CSS3"],
features: [
"Design responsive",
"Animations fluides",
"Mode sombre/clair",
"Performance optimisée"
],
color: "#2196F3",
icon: <ExternalLink size={24} />,
links: {
github: "https://github.com/dayronvanleemput/portfolio", // Remplacez par votre lien
demo: "https://dayronvanleemput.dev" // Remplacez par votre lien
}
}
];
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.3,
delayChildren: 0.2
}
}
};
const projectVariants = {
hidden: { opacity: 0, y: 50 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.8,
ease: [0.25, 0.1, 0.25, 1] as const
}
}
};
return (
<section id="projects" className="projects">
<div className="container">
<motion.div
className="section-header"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
viewport={{ once: true }}
>
<h2 className="section-title">Mes Projets</h2>
<p className="section-subtitle">
Découvrez les projets sur lesquels j'ai travaillé et qui me tiennent à cœur
</p>
</motion.div>
<motion.div
className="projects-grid"
variants={containerVariants}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
{projects.map((project, index) => (
<motion.div
key={project.id}
className="project-card"
variants={projectVariants}
whileHover={{
y: -10,
scale: 1.02,
transition: { duration: 0.3 }
}}
>
<div className="project-header">
<div
className="project-icon"
style={{ backgroundColor: `${project.color}20`, color: project.color }}
>
{project.icon}
</div>
<div className="project-status">
<span className="status-badge" style={{ backgroundColor: `${project.color}20`, color: project.color }}>
{project.status}
</span>
</div>
</div>
<div className="project-content">
<h3 className="project-title">{project.title}</h3>
<p className="project-description">{project.description}</p>
<div className="project-technologies">
{project.technologies.map((tech, techIndex) => (
<motion.span
key={tech}
className="tech-tag"
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{
duration: 0.3,
delay: (index * 0.1) + (techIndex * 0.05)
}}
viewport={{ once: true }}
>
{tech}
</motion.span>
))}
</div>
<div className="project-features">
<h4>Fonctionnalités principales :</h4>
<ul>
{project.features.map((feature, featureIndex) => (
<motion.li
key={featureIndex}
initial={{ opacity: 0, x: -20 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{
duration: 0.5,
delay: (index * 0.2) + (featureIndex * 0.1)
}}
viewport={{ once: true }}
>
{feature}
</motion.li>
))}
</ul>
</div>
</div>
<div className="project-footer">
<div className="project-links">
{project.links.github !== "#" && (
<motion.a
href={project.links.github}
target="_blank"
rel="noopener noreferrer"
className="project-link"
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
>
<Github size={20} />
Code
</motion.a>
)}
{project.links.demo !== "#" && (
<motion.a
href={project.links.demo}
target="_blank"
rel="noopener noreferrer"
className="project-link primary"
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
>
<ExternalLink size={20} />
Voir le projet
</motion.a>
)}
</div>
</div>
</motion.div>
))}
</motion.div>
{/* Section des projets futurs */}
<motion.div
className="future-projects"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.3 }}
viewport={{ once: true }}
>
<h3 className="future-title">Projets à venir</h3>
<div className="future-projects-grid">
{[
{
title: "Application de gestion de tâches",
description: "Une app de productivité avec synchronisation cloud",
tech: "Flutter • Firebase • Bloc"
},
{
title: "API REST e-commerce",
description: "Backend complet pour application e-commerce",
tech: "Java • Spring Boot • PostgreSQL"
},
{
title: "Dashboard analytique",
description: "Interface web pour visualiser des données",
tech: "React • D3.js • TypeScript"
}
].map((futureProject, index) => (
<motion.div
key={index}
className="future-project-card"
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
whileHover={{
scale: 1.02,
y: -2,
transition: { duration: 0.2 }
}}
viewport={{ once: true }}
>
<div className="future-project-icon">
<Calendar size={20} />
</div>
<h4>{futureProject.title}</h4>
<p>{futureProject.description}</p>
<span className="future-tech">{futureProject.tech}</span>
</motion.div>
))}
</div>
</motion.div>
</div>
</section>
);
};
export default Projects;

191
src/components/Skills.tsx Normal file
View File

@@ -0,0 +1,191 @@
import { motion } from 'framer-motion';
import { Code, Database, Smartphone, Globe, Server, Wrench } from 'lucide-react';
const Skills = () => {
const skillCategories = [
{
icon: <Smartphone size={32} />,
title: "Mobile",
color: "#4FC3F7",
skills: [
{ name: "Dart", level: 85, color: "#0175C2" },
{ name: "Flutter", level: 80, color: "#02569B" }
]
},
{
icon: <Globe size={32} />,
title: "Frontend",
color: "#42A5F5",
skills: [
{ name: "React", level: 75, color: "#61DAFB" },
{ name: "TypeScript", level: 70, color: "#3178C6" },
{ name: "JavaScript", level: 80, color: "#F7DF1E" }
]
},
{
icon: <Server size={32} />,
title: "Backend",
color: "#66BB6A",
skills: [
{ name: "Java", level: 75, color: "#ED8B00" },
{ name: "C#", level: 65, color: "#239120" }
]
},
{
icon: <Database size={32} />,
title: "Outils & Autres",
color: "#AB47BC",
skills: [
{ name: "Git", level: 70, color: "#F05032" },
{ name: "VS Code", level: 90, color: "#007ACC" },
{ name: "Android Studio", level: 75, color: "#3DDC84" }
]
}
];
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.2,
delayChildren: 0.3
}
}
};
const categoryVariants = {
hidden: { opacity: 0, y: 50 },
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.6,
ease: [0.25, 0.1, 0.25, 1] as const
}
}
};
return (
<section id="skills" className="skills">
<div className="container">
<motion.div
className="section-header"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8 }}
viewport={{ once: true }}
>
<h2 className="section-title">Compétences & Technologies</h2>
<p className="section-subtitle">
Les technologies que je maîtrise et avec lesquelles j'aime travailler
</p>
</motion.div>
<motion.div
className="skills-grid"
variants={containerVariants}
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
>
{skillCategories.map((category, categoryIndex) => (
<motion.div
key={category.title}
className="skill-category"
variants={categoryVariants}
whileHover={{
scale: 1.02,
y: -5,
transition: { duration: 0.3 }
}}
>
<div className="category-header">
<div
className="category-icon"
style={{ backgroundColor: `${category.color}20`, color: category.color }}
>
{category.icon}
</div>
<h3 className="category-title">{category.title}</h3>
</div>
<div className="skills-list">
{category.skills.map((skill, skillIndex) => (
<motion.div
key={skill.name}
className="skill-item"
initial={{ opacity: 0, x: -30 }}
whileInView={{ opacity: 1, x: 0 }}
transition={{
duration: 0.5,
delay: (categoryIndex * 0.2) + (skillIndex * 0.1)
}}
viewport={{ once: true }}
>
<div className="skill-header">
<span className="skill-name">{skill.name}</span>
<span className="skill-percentage">{skill.level}%</span>
</div>
<div className="skill-bar">
<motion.div
className="skill-progress"
style={{ backgroundColor: skill.color }}
initial={{ width: 0 }}
whileInView={{ width: `${skill.level}%` }}
transition={{
duration: 1.5,
delay: (categoryIndex * 0.2) + (skillIndex * 0.1) + 0.5,
ease: [0.25, 0.1, 0.25, 1] as const
}}
viewport={{ once: true }}
/>
</div>
</motion.div>
))}
</div>
</motion.div>
))}
</motion.div>
{/* Section des soft skills */}
<motion.div
className="soft-skills"
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.3 }}
viewport={{ once: true }}
>
<h3 className="soft-skills-title">Autres compétences</h3>
<div className="soft-skills-grid">
{[
{ name: "Résolution de problèmes", icon: <Wrench size={20} /> },
{ name: "Travail en équipe", icon: <Code size={20} /> },
{ name: "Apprentissage continu", icon: <Database size={20} /> },
{ name: "Communication", icon: <Globe size={20} /> }
].map((softSkill, index) => (
<motion.div
key={softSkill.name}
className="soft-skill-tag"
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
whileHover={{
scale: 1.05,
transition: { duration: 0.2 }
}}
viewport={{ once: true }}
>
{softSkill.icon}
<span>{softSkill.name}</span>
</motion.div>
))}
</div>
</motion.div>
</div>
</section>
);
};
export default Skills;