feat: enhance WelcomeSection with animations and particle effects

- Added animation states for welcome message, time message, and greeting to improve user engagement.
- Introduced particle effects for a dynamic background experience.
- Refactored button to trigger message refresh with animations, enhancing interactivity.
- Updated styles for improved visual appeal and responsiveness.
This commit is contained in:
Julien Froidefond
2025-10-01 21:24:45 +02:00
parent e2527ca88a
commit c104fc0e11

View File

@@ -1,7 +1,7 @@
'use client'; 'use client';
import { useSession } from 'next-auth/react'; import { useSession } from 'next-auth/react';
import { useState, useEffect } from 'react'; import { useState, useEffect, useRef } from 'react';
const WELCOME_GREETINGS = [ const WELCOME_GREETINGS = [
"Bienvenue", "Bienvenue",
@@ -134,14 +134,32 @@ export function WelcomeSection() {
const [welcomeMessage, setWelcomeMessage] = useState<string>(''); const [welcomeMessage, setWelcomeMessage] = useState<string>('');
const [timeMessage, setTimeMessage] = useState<string>(''); const [timeMessage, setTimeMessage] = useState<string>('');
const [greeting, setGreeting] = useState<string>(''); const [greeting, setGreeting] = useState<string>('');
const [isAnimating, setIsAnimating] = useState(false);
const [particleCount, setParticleCount] = useState(0);
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
// Générer un message de bienvenue aléatoire // Générer un message de bienvenue aléatoire
setWelcomeMessage(getRandomWelcomeMessage()); setWelcomeMessage(getRandomWelcomeMessage());
setTimeMessage(getTimeBasedMessage()); setTimeMessage(getTimeBasedMessage());
setGreeting(getRandomGreeting()); setGreeting(getRandomGreeting());
// Animation d'entrée
setTimeout(() => setIsAnimating(true), 100);
}, []); }, []);
const handleRefresh = () => {
setIsAnimating(false);
setParticleCount(prev => prev + 1);
setTimeout(() => {
setWelcomeMessage(getRandomWelcomeMessage());
setTimeMessage(getTimeBasedMessage());
setGreeting(getRandomGreeting());
setIsAnimating(true);
}, 300);
};
if (!session?.user) { if (!session?.user) {
return null; return null;
} }
@@ -151,63 +169,165 @@ export function WelcomeSection() {
session.user.email; session.user.email;
return ( return (
<div className="bg-gradient-to-r from-[var(--primary)]/10 via-[var(--accent)]/10 to-[var(--purple)]/10 rounded-xl p-6 mb-8 border border-[var(--primary)]/20"> <div
<div className="flex flex-col md:flex-row items-center md:items-start gap-4"> ref={containerRef}
{/* Avatar */} className="relative overflow-hidden bg-gradient-to-br from-[var(--primary)]/5 via-[var(--accent)]/8 to-[var(--purple)]/5 rounded-2xl p-8 mb-8 border border-[var(--primary)]/20 backdrop-blur-sm"
<div className="flex-shrink-0"> style={{
background: `
radial-gradient(circle at 20% 80%, color-mix(in srgb, var(--primary) 15%, transparent) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, color-mix(in srgb, var(--accent) 12%, transparent) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, color-mix(in srgb, var(--purple) 10%, transparent) 0%, transparent 50%),
linear-gradient(135deg, var(--card) 0%, color-mix(in srgb, var(--card) 95%, var(--primary)) 100%)
`
}}
>
{/* Particules animées */}
<div className="absolute inset-0 pointer-events-none">
{Array.from({ length: 12 }).map((_, i) => (
<div
key={`particle-${particleCount}-${i}`}
className="absolute w-1 h-1 rounded-full opacity-60"
style={{
backgroundColor: `var(--${['primary', 'accent', 'purple', 'success'][i % 4]})`,
left: `${Math.random() * 100}%`,
top: `${Math.random() * 100}%`,
animation: `float ${3 + Math.random() * 4}s ease-in-out infinite`,
animationDelay: `${Math.random() * 2}s`
}}
/>
))}
</div>
{/* Effet de brillance */}
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent transform -skew-x-12 animate-shimmer" />
<div className="relative z-10 flex flex-col lg:flex-row items-center lg:items-start gap-6">
{/* Avatar avec animations */}
<div className="flex-shrink-0 relative group">
<div className="absolute inset-0 bg-gradient-to-r from-[var(--primary)] via-[var(--accent)] to-[var(--purple)] rounded-full blur-lg opacity-30 group-hover:opacity-50 transition-opacity duration-500 animate-pulse" />
{session.user.avatar ? ( {session.user.avatar ? (
<div className="relative"> <div className="relative">
<div className="absolute inset-0 bg-gradient-to-r from-[var(--primary)] to-[var(--accent)] rounded-full animate-spin-slow opacity-20" />
{/* eslint-disable-next-line @next/next/no-img-element */} {/* eslint-disable-next-line @next/next/no-img-element */}
<img <img
src={session.user.avatar} src={session.user.avatar}
alt="Avatar" alt="Avatar"
className="w-16 h-16 rounded-full object-cover border-3 border-[var(--primary)]/30 shadow-lg" className="relative w-20 h-20 rounded-full object-cover border-4 border-[var(--primary)]/40 shadow-2xl transition-all duration-500 group-hover:scale-110 group-hover:border-[var(--accent)]/60"
/> />
<div className="absolute -bottom-1 -right-1 w-6 h-6 bg-[var(--success)] rounded-full border-3 border-[var(--card)] flex items-center justify-center"> <div className="absolute -bottom-2 -right-2 w-7 h-7 bg-gradient-to-r from-[var(--success)] to-[var(--green)] rounded-full border-4 border-[var(--card)] flex items-center justify-center shadow-lg">
<svg className="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20"> <svg className="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" /> <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg> </svg>
</div> </div>
</div> </div>
) : ( ) : (
<div className="w-16 h-16 rounded-full bg-[var(--primary)]/20 border-3 border-[var(--primary)]/30 flex items-center justify-center"> <div className="relative w-20 h-20 rounded-full bg-gradient-to-br from-[var(--primary)]/30 to-[var(--accent)]/30 border-4 border-[var(--primary)]/40 flex items-center justify-center shadow-2xl transition-all duration-500 group-hover:scale-110 group-hover:border-[var(--accent)]/60">
<svg className="w-8 h-8 text-[var(--primary)]" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg className="w-10 h-10 text-[var(--primary)] transition-colors duration-500 group-hover:text-[var(--accent)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg> </svg>
</div> </div>
)} )}
</div> </div>
{/* Message de bienvenue */} {/* Message de bienvenue avec animations */}
<div className="flex-1 text-center md:text-left"> <div className="flex-1 text-center lg:text-left space-y-3">
<h1 className="text-2xl md:text-3xl font-mono font-bold text-[var(--foreground)] mb-2"> <div className="overflow-hidden">
{greeting}, {displayName} ! <h1
</h1> className={`text-3xl lg:text-4xl font-bold transition-all duration-700 ${
<p className="text-lg text-[var(--muted-foreground)] mb-2"> isAnimating ? 'translate-y-0 opacity-100' : 'translate-y-4 opacity-0'
{timeMessage} }`}
</p> style={{ transitionDelay: '0.1s' }}
<p className="text-base text-[var(--primary)] font-medium"> >
{welcomeMessage} <span className="inline-block animate-wave text-[var(--foreground)]" style={{ animationDelay: '0.1s' }}>
</p> {greeting}
</span>
<span className="mx-2 text-[var(--primary)] animate-pulse">,</span>
<span className="inline-block animate-wave text-[var(--foreground)]" style={{ animationDelay: '0.3s' }}>
{displayName}
</span>
<span className="ml-2 text-[var(--accent)] animate-bounce">!</span>
</h1>
</div>
<div className="overflow-hidden">
<p
className={`text-lg text-[var(--muted-foreground)] transition-all duration-700 ${
isAnimating ? 'translate-y-0 opacity-100' : 'translate-y-4 opacity-0'
}`}
style={{ transitionDelay: '0.3s' }}
>
{timeMessage}
</p>
</div>
<div className="overflow-hidden">
<p
className={`text-base font-medium bg-gradient-to-r from-[var(--primary)] to-[var(--accent)] bg-clip-text text-transparent transition-all duration-700 ${
isAnimating ? 'translate-y-0 opacity-100' : 'translate-y-4 opacity-0'
}`}
style={{ transitionDelay: '0.5s' }}
>
{welcomeMessage}
</p>
</div>
</div> </div>
{/* Bouton pour changer le message */} {/* Bouton avec animations avancées */}
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<button <button
onClick={() => { onClick={handleRefresh}
setWelcomeMessage(getRandomWelcomeMessage()); className="group relative p-4 rounded-2xl bg-gradient-to-br from-[var(--card)] to-[var(--card-hover)] border border-[var(--border)] hover:border-[var(--primary)]/40 transition-all duration-500 hover:shadow-2xl hover:shadow-[var(--primary)]/20 hover:-translate-y-1"
setTimeMessage(getTimeBasedMessage());
setGreeting(getRandomGreeting());
}}
className="p-2 rounded-lg bg-[var(--card)] border border-[var(--border)] hover:bg-[var(--card-hover)] transition-colors"
title="Changer le message" title="Changer le message"
> >
<svg className="w-5 h-5 text-[var(--muted-foreground)]" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <div className="absolute inset-0 bg-gradient-to-r from-[var(--primary)]/10 to-[var(--accent)]/10 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /> <div className="relative">
</svg> <svg
className="w-6 h-6 text-[var(--muted-foreground)] group-hover:text-[var(--primary)] transition-all duration-500 group-hover:rotate-180"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
</div>
</button> </button>
</div> </div>
</div> </div>
<style jsx>{`
@keyframes float {
0%, 100% { transform: translateY(0px) rotate(0deg); }
50% { transform: translateY(-20px) rotate(180deg); }
}
@keyframes shimmer {
0% { transform: translateX(-100%) skewX(-12deg); }
100% { transform: translateX(200%) skewX(-12deg); }
}
@keyframes wave {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
@keyframes spin-slow {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.animate-shimmer {
animation: shimmer 3s ease-in-out infinite;
}
.animate-wave {
animation: wave 2s ease-in-out infinite;
}
.animate-spin-slow {
animation: spin-slow 8s linear infinite;
}
`}</style>
</div> </div>
); );
} }