feat: Add metrics on homepage

This commit is contained in:
kbe
2025-08-17 23:39:24 +02:00
parent 17e6711299
commit ba3f36a5e8
5 changed files with 200 additions and 3 deletions

View File

@@ -1,3 +1,16 @@
// Entry point for the build script in your package.json // Entry point for the build script in your package.json
import "@hotwired/turbo-rails" import "@hotwired/turbo-rails"
import "./controllers" import "./controllers"
import Counter from "./components/counter"
// Initialize counters when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
const counters = document.querySelectorAll('.counter')
counters.forEach(counter => new Counter(counter))
})
// Re-initialize counters on Turbo page loads
document.addEventListener('turbo:load', () => {
const counters = document.querySelectorAll('.counter')
counters.forEach(counter => new Counter(counter))
})

View File

@@ -0,0 +1,61 @@
export default class Counter {
constructor(element) {
this.element = element
this.target = parseFloat(element.dataset.target)
this.decimal = element.dataset.decimal === 'true'
this.duration = 2000
this.startTime = null
this.init()
}
init() {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.animate()
observer.unobserve(this.element)
}
})
}, { threshold: 0.5 })
observer.observe(this.element)
}
animate() {
const startValue = 0
const startTime = performance.now()
const updateCounter = (currentTime) => {
const elapsedTime = currentTime - startTime
const progress = Math.min(elapsedTime / this.duration, 1)
// Easing function for smooth animation
const easeOutQuart = 1 - Math.pow(1 - progress, 4)
let currentValue = startValue + (this.target - startValue) * easeOutQuart
if (this.decimal && this.target < 10) {
currentValue = currentValue.toFixed(1)
} else {
currentValue = Math.floor(currentValue)
}
this.element.textContent = currentValue
if (progress < 1) {
requestAnimationFrame(updateCounter)
} else {
this.element.textContent = this.decimal && this.target < 10 ? this.target.toFixed(1) : this.target
}
}
requestAnimationFrame(updateCounter)
}
}
// Initialize all counters
document.addEventListener('DOMContentLoaded', () => {
const counters = document.querySelectorAll('.counter')
counters.forEach(counter => new Counter(counter))
})

View File

@@ -0,0 +1,2 @@

View File

@@ -3,9 +3,9 @@
<div class="absolute inset-0 bg-black bg-opacity-40"></div> <div class="absolute inset-0 bg-black bg-opacity-40"></div>
<div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center"> <div class="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<h1 class="text-5xl md:text-7xl font-bold text-white mb-6 leading-tight"> <h1 class="text-5xl md:text-7xl font-bold text-white mb-6 leading-tight">
Découvrez la nuit parisienne Découvrez les afterworks et soirée
<span class="block text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-400"> <span class="text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-pink-400">
comme jamais à Paris
</span> </span>
</h1> </h1>
<p class="text-xl md:text-2xl text-gray-200 mb-8 max-w-3xl mx-auto leading-relaxed"> <p class="text-xl md:text-2xl text-gray-200 mb-8 max-w-3xl mx-auto leading-relaxed">
@@ -18,6 +18,101 @@
</div> </div>
</section> </section>
<!-- Metrics -->
<section class="bg-gradient-to-br from-gray-900 via-gray-800 to-black py-20">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="text-center mb-16">
<h2 class="text-4xl md:text-5xl font-bold text-white mb-4">
Des chiffres qui parlent
</h2>
<p class="text-xl text-gray-300 max-w-2xl mx-auto">
La plateforme préférée des Parisiens pour vivre la nuit
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 md:gap-12">
<!-- Total Events -->
<div class="group relative">
<div class="relative overflow-hidden rounded-2xl bg-gray-800/60 backdrop-blur-sm border border-gray-700/50 hover:border-purple-500/50 transition-all duration-300 p-8">
<div class="absolute inset-0 bg-gradient-to-br from-purple-600/20 to-indigo-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div class="relative">
<div class="text-5xl md:text-6xl font-light bg-gradient-to-r from-purple-400 via-indigo-400 to-pink-400 bg-clip-text text-transparent mb-3">
<span class="counter" data-target="2473">0</span>
</div>
<p class="text-gray-200 font-mono uppercase tracking-widest text-sm font-medium">
Événements organisés
</p>
<div class="mt-4 h-1 bg-gradient-to-r from-purple-500 via-indigo-500 to-pink-500 rounded-full w-0 group-hover:w-full transition-all duration-500"></div>
</div>
</div>
</div>
<!-- Total Users -->
<div class="group relative">
<div class="relative overflow-hidden rounded-2xl bg-gray-800/60 backdrop-blur-sm border border-gray-700/50 hover:border-purple-500/50 transition-all duration-300 p-8">
<div class="absolute inset-0 bg-gradient-to-br from-purple-600/20 to-indigo-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div class="relative">
<div class="text-5xl md:text-6xl font-light bg-gradient-to-r from-purple-400 via-indigo-400 to-pink-400 bg-clip-text text-transparent mb-3">
<span class="counter" data-target="15420">0</span>+
</div>
<p class="text-gray-200 font-mono uppercase tracking-widest text-sm font-medium">
Membres actifs
</p>
<div class="mt-4 h-1 bg-gradient-to-r from-purple-500 via-indigo-500 to-pink-500 rounded-full w-0 group-hover:w-full transition-all duration-500"></div>
</div>
</div>
</div>
<!-- Average Rating -->
<div class="group relative">
<div class="relative overflow-hidden rounded-2xl bg-gray-800/60 backdrop-blur-sm border border-gray-700/50 hover:border-purple-500/50 transition-all duration-300 p-8">
<div class="absolute inset-0 bg-gradient-to-br from-purple-600/20 to-indigo-600/20 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
<div class="relative">
<div class="text-5xl md:text-6xl font-light bg-gradient-to-r from-purple-400 via-indigo-400 to-pink-400 bg-clip-text text-transparent mb-3">
<span class="counter" data-target="4.8" data-decimal="true">0</span>/5
</div>
<p class="text-gray-200 font-mono uppercase tracking-widest text-sm font-medium">
Note moyenne des soirées
</p>
<div class="mt-4 h-1 bg-gradient-to-r from-purple-500 via-indigo-500 to-pink-500 rounded-full w-0 group-hover:w-full transition-all duration-500"></div>
</div>
</div>
</div>
</div>
<!-- Additional Stats Row -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-6 mt-12">
<div class="text-center">
<div class="text-3xl font-bold text-purple-300">
<span class="counter" data-target="89">0</span>%
</div>
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Taux de remplissage</p>
</div>
<div class="text-center">
<div class="text-3xl font-bold text-purple-300">
<span class="counter" data-target="12">0</span>
</div>
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Arrondissements</p>
</div>
<div class="text-center">
<div class="text-3xl font-bold text-purple-300">
<span class="counter" data-target="156">0</span>
</div>
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Établissements partenaires</p>
</div>
<div class="text-center">
<div class="text-3xl font-bold text-purple-300">
<span class="counter" data-target="98">0</span>%
</div>
<p class="text-gray-300 text-sm font-mono uppercase tracking-wide font-medium">Satisfaction client</p>
</div>
</div>
</div>
</section>
<!-- Quick Search Section --> <!-- Quick Search Section -->
<section id="search" class="bg-gray-900 py-16"> <section id="search" class="bg-gray-900 py-16">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">

26
ecosystem.config.js Normal file
View File

@@ -0,0 +1,26 @@
module.exports = {
apps: [
{
name: "watch-js", // Name of the process
script: "bun", // Run npm command
args: "run build --watch", // Run build, build:css, and watch:files in sequence
watch: false, // Watch for file changes (you can modify this as needed)
autorestart: true, // Automatically restart on crash
max_restarts: 10, // Maximum number of restarts PM2 will attempt
env: {
NODE_ENV: "development", // Set environment variables here (optional)
},
},
{
name: "watch-css", // Name of the process
script: "bun", // Run npm command
args: "run build:css --watch", // Run build, build:css, and watch:files in sequence
watch: false, // Watch for file changes (you can modify this as needed)
autorestart: true, // Automatically restart on crash
max_restarts: 10, // Maximum number of restarts PM2 will attempt
env: {
NODE_ENV: "development", // Set environment variables here (optional)
},
},
],
};