first commit

This commit is contained in:
Puechberty Arthur
2026-03-30 23:07:36 +02:00
commit 49fd31f4db
36 changed files with 30532 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
'use client';
import { useState, useEffect, useMemo } from 'react';
import { useLocale } from './LocaleProvider';
import { ts } from '@/lib/i18n';
import { getMoonPhaseInfo, getNextFullMoon, FULL_MOON_NAMES } from '@/lib/lunar';
function computeCountdown(target: Date) {
const now = new Date();
const totalMs = target.getTime() - now.getTime();
if (totalMs <= 0) return { days: 0, hours: 0, minutes: 0, seconds: 0 };
return {
days: Math.floor(totalMs / (1000 * 60 * 60 * 24)),
hours: Math.floor((totalMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)),
minutes: Math.floor((totalMs % (1000 * 60 * 60)) / (1000 * 60)),
seconds: Math.floor((totalMs % (1000 * 60)) / 1000),
};
}
export default function HeroSection() {
const { locale } = useLocale();
const nextFull = useMemo(() => getNextFullMoon(), []);
const currentPhase = useMemo(() => getMoonPhaseInfo(new Date()), []);
const [countdown, setCountdown] = useState(() => computeCountdown(nextFull));
const [mounted, setMounted] = useState(false);
useEffect(() => {
const mountTimer = setTimeout(() => setMounted(true), 0);
const updateCountdown = () => {
setCountdown(computeCountdown(nextFull));
};
const timer = setInterval(updateCountdown, 1000);
return () => {
clearTimeout(mountTimer);
clearInterval(timer);
};
}, [nextFull]);
const nextFullName =
FULL_MOON_NAMES[nextFull.getUTCMonth() + 1]?.[locale as keyof typeof FULL_MOON_NAMES[1]] ||
FULL_MOON_NAMES[nextFull.getUTCMonth() + 1]?.en;
const formatDate = (d: Date) => {
try {
return d.toLocaleDateString(locale, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
} catch {
return d.toLocaleDateString('en', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
}
};
return (
<section id="hero" aria-label="Moon Phases Hero" className="relative min-h-screen flex items-center justify-center overflow-hidden pt-16">
{/* Moon glow */}
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 -translate-y-1/2 w-75 h-75 md:w-125 md:h-125 rounded-full gradient-radial-glow blur-3xl pointer-events-none" />
<div className="section-container px-3 sm:px-6 text-center relative z-10">
{/* Current phase display */}
<div className="mb-4 sm:mb-6 inline-flex max-w-full flex-wrap items-center justify-center gap-2 sm:gap-3 px-3 sm:px-5 py-2 sm:py-2.5 rounded-full bg-white/5 border border-white/10">
<span className="text-2xl sm:text-3xl">{currentPhase.emoji}</span>
<span className="text-xs sm:text-sm text-white/60 leading-snug wrap-break-word">
{ts('current_phase', locale)}: {ts(currentPhase.phaseName, locale)} {currentPhase.illumination}%
</span>
</div>
<h1 className="text-2xl sm:text-5xl md:text-7xl font-bold mb-3 sm:mb-6 leading-tight">
<span className="gradient-text-hero">
{ts('hero_title', locale)}
</span>
</h1>
<p className="text-base md:text-xl text-white/50 max-w-2xl mx-auto mb-8 sm:mb-12 leading-relaxed">
{ts('hero_subtitle', locale)}
</p>
{/* Next Full Moon Card */}
<div className="glass-card w-full max-w-lg mx-auto p-4 sm:p-6 md:p-8 mb-8">
<h2 className="text-sm uppercase tracking-widest text-indigo-300 mb-3">{ts('next_full_moon', locale)}</h2>
<div className="text-3xl mb-1">🌕</div>
<>
<p className="text-lg sm:text-xl font-semibold text-yellow-200 mb-1">{nextFullName}</p>
<p className="text-white/60 text-xs sm:text-sm mb-4 sm:mb-6 wrap-break-word">{mounted ? formatDate(nextFull) : ''}</p>
</>
{/* Countdown */}
<div className="flex flex-wrap justify-center gap-1.5 sm:gap-3 md:gap-4">
{[
{ value: countdown.days, label: ts('countdown_days', locale) },
{ value: countdown.hours, label: ts('countdown_hours', locale) },
{ value: countdown.minutes, label: ts('countdown_minutes', locale) },
{ value: countdown.seconds, label: ts('countdown_seconds', locale) },
].map((item, i) => (
<div key={i} className="countdown-box">
<span className="countdown-number">{mounted ? String(item.value).padStart(2, '0') : '--'}</span>
<span className="countdown-label">{item.label}</span>
</div>
))}
</div>
</div>
{/* CTA */}
<a href="#calendar" className="glow-btn inline-block text-base">
{ts('explore', locale)}
</a>
</div>
</section>
);
}