Initialize Next.js project with essential configurations and components
- Added .gitignore to exclude unnecessary files and directories. - Created next.config.js for Next.js configuration. - Set up package.json and package-lock.json with dependencies including Next.js, React, and TypeScript. - Implemented Tailwind CSS for styling with a dedicated tailwind.config.ts. - Developed core application structure including layout, pages, and components for header, footer, and various sections (Hero, Services, Pricing, etc.). - Integrated contact form functionality using nodemailer for email handling. - Established global styles in globals.css and added animations with Framer Motion. - Included README.md for project documentation and setup instructions.
This commit is contained in:
75
components/sections/Stats.tsx
Normal file
75
components/sections/Stats.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { useInView } from 'framer-motion'
|
||||
import { useRef, useState, useEffect } from 'react'
|
||||
import { Lock, Clock, Ban, CheckCircle } from 'lucide-react'
|
||||
import BackgroundAnimations from '@/components/BackgroundAnimations'
|
||||
|
||||
export default function Stats() {
|
||||
const ref = useRef(null)
|
||||
const isInView = useInView(ref, { once: true, margin: '-100px' })
|
||||
|
||||
// Générer un nombre aléatoire pour les tentatives bloquées
|
||||
// Initialiser à 0 pour éviter les erreurs d'hydratation (serv/client doivent correspondre)
|
||||
const [blockedAttempts, setBlockedAttempts] = useState<number>(0)
|
||||
|
||||
// Mettre à jour le nombre aléatoire après le montage et périodiquement
|
||||
useEffect(() => {
|
||||
// Générer le premier nombre aléatoire uniquement côté client
|
||||
const randomValue = Math.floor(Math.random() * 10000)
|
||||
setBlockedAttempts(randomValue)
|
||||
|
||||
// Mettre à jour le nombre aléatoire périodiquement pour donner un effet dynamique
|
||||
const interval = setInterval(() => {
|
||||
// Générer un nouveau nombre aléatoire entre 0 et 9999
|
||||
const newRandomValue = Math.floor(Math.random() * 10000)
|
||||
setBlockedAttempts(newRandomValue)
|
||||
}, 5000) // Mise à jour toutes les 5 secondes
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
const stats = [
|
||||
{ icon: Clock, value: '99.9', unit: '%', label: 'Uptime', gradient: 'from-blue-500 to-cyan-500', isDynamic: false },
|
||||
{ icon: Lock, value: '256', unit: ' Bit', label: 'Encryption', gradient: 'from-security-accent to-green-500', isDynamic: false },
|
||||
{ icon: Clock, value: '24', unit: '/7', label: 'Support', gradient: 'from-purple-500 to-pink-500', isDynamic: false },
|
||||
{ icon: Ban, value: blockedAttempts.toString(), label: 'Tentatives bloquées', gradient: 'from-red-500 to-orange-500', isDynamic: true },
|
||||
{ icon: CheckCircle, value: '100', unit: '%', label: 'Conformité RGPD', gradient: 'from-emerald-500 to-teal-500', isDynamic: false },
|
||||
]
|
||||
|
||||
return (
|
||||
<section className="section-padding bg-security-dark relative overflow-hidden">
|
||||
<BackgroundAnimations variant="grid" />
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
|
||||
<div ref={ref} className="grid grid-cols-2 md:grid-cols-5 gap-4 md:gap-6">
|
||||
{stats.map((stat, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, y: 30, rotateY: -15 }}
|
||||
animate={isInView ? { opacity: 1, y: 0, rotateY: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
className="text-center p-6 card-dark card-hover relative overflow-hidden group"
|
||||
>
|
||||
{/* Gradient Background on Hover */}
|
||||
<div className={`absolute inset-0 bg-gradient-to-br ${stat.gradient} opacity-0 group-hover:opacity-10 transition-opacity duration-300`} />
|
||||
|
||||
<div className={`w-14 h-14 bg-gradient-to-br ${stat.gradient} rounded-xl flex items-center justify-center mx-auto mb-4 relative z-10 shadow-lg`}>
|
||||
<stat.icon className="w-7 h-7 text-white" />
|
||||
</div>
|
||||
<div className="mb-2 relative z-10">
|
||||
<span className="text-3xl md:text-4xl font-bold text-white">
|
||||
{stat.isDynamic ? blockedAttempts.toString() : (isInView ? stat.value : '0')}
|
||||
</span>
|
||||
{stat.unit && (
|
||||
<span className="text-xl text-gray-400">{stat.unit}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-sm text-gray-400 font-medium relative z-10">{stat.label}</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user