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:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user