400 lines
15 KiB
TypeScript
400 lines
15 KiB
TypeScript
"use client";
|
||
|
||
import { useTranslations } from 'next-intl';
|
||
import { motion } from 'framer-motion';
|
||
import { Calendar, Music, Disc3, CalendarDays, Zap, GlassWater } from 'lucide-react';
|
||
|
||
/* ── static data ───────────────────────────────── */
|
||
|
||
const WEEK_DAYS = ['Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt', 'Paz'];
|
||
const ACTIVE_DAYS: Record<number, string> = {
|
||
3: '#FF5A36',
|
||
4: '#00E5FF',
|
||
5: '#00E5FF',
|
||
};
|
||
|
||
/* ── types ─────────────────────────────────────── */
|
||
type EventItem = {
|
||
id: string;
|
||
title: string;
|
||
time: string;
|
||
tag: string;
|
||
iconType: string;
|
||
colorTheme: string;
|
||
isFeatured: boolean;
|
||
};
|
||
|
||
/* ── sub-components ────────────────────────────── */
|
||
|
||
function VinylDisc() {
|
||
return (
|
||
<motion.div
|
||
animate={{ rotate: 360 }}
|
||
transition={{ duration: 4, repeat: Infinity, ease: 'linear' }}
|
||
className="w-36 h-36 md:w-44 md:h-44 rounded-full relative flex-shrink-0"
|
||
style={{
|
||
background:
|
||
'repeating-conic-gradient(#0a0f1e 0deg 18deg, #141e30 18deg 36deg)',
|
||
boxShadow: '0 0 48px rgba(0,229,255,0.35), 0 0 80px rgba(0,229,255,0.12)',
|
||
}}
|
||
>
|
||
{/* Grooves */}
|
||
{[55, 45, 35].map((size) => (
|
||
<div
|
||
key={size}
|
||
className="absolute rounded-full border border-white/[0.04]"
|
||
style={{ inset: `${(100 - size) / 2}%` }}
|
||
/>
|
||
))}
|
||
{/* Label */}
|
||
<div
|
||
className="absolute inset-[35%] rounded-full flex items-center justify-center"
|
||
style={{ background: 'radial-gradient(circle, #00E5FF, #0084A8)' }}
|
||
>
|
||
<div className="w-2 h-2 rounded-full bg-white/80" />
|
||
</div>
|
||
</motion.div>
|
||
);
|
||
}
|
||
|
||
function EqBars({ color }: { color: string }) {
|
||
const heights = [0.4, 0.8, 0.5, 1, 0.65, 0.9, 0.45, 0.75, 0.55, 0.85];
|
||
return (
|
||
<div className="flex items-end gap-[3px] h-8">
|
||
{heights.map((h, i) => (
|
||
<motion.div
|
||
key={i}
|
||
className="w-1.5 rounded-full origin-bottom"
|
||
style={{ background: color, height: `${h * 100}%` }}
|
||
animate={{ scaleY: [h, h > 0.6 ? 0.2 : 1, h] }}
|
||
transition={{
|
||
duration: 0.5 + Math.random() * 0.4,
|
||
repeat: Infinity,
|
||
repeatType: 'mirror',
|
||
delay: i * 0.07,
|
||
ease: 'easeInOut',
|
||
}}
|
||
/>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function WineGlassIcon() {
|
||
return (
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
width="22"
|
||
height="22"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
strokeWidth="2"
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
>
|
||
<path d="M8 22h8" />
|
||
<path d="M12 15v7" />
|
||
<path d="M12 15a5 5 0 0 0 5-5c0-2-.5-4-2-8H9c-1.5 4-2 6-2 8a5 5 0 0 0 5 5Z" />
|
||
<path d="M7 10h10" />
|
||
</svg>
|
||
);
|
||
}
|
||
|
||
/* ── main component ────────────────────────────── */
|
||
|
||
export default function Events({ dbEvents = [] }: { dbEvents?: any[] }) {
|
||
const t = useTranslations('Events');
|
||
|
||
// Hardcoded fallback data if db is empty
|
||
const defaultEvents: EventItem[] = dbEvents.length > 0 ? dbEvents : [
|
||
{
|
||
id: "fallback-1",
|
||
title: t('cards.dj.title'),
|
||
time: t('cards.dj.time'),
|
||
tag: "EN POPÜLER",
|
||
iconType: "dj",
|
||
colorTheme: "cyan",
|
||
isFeatured: true,
|
||
},
|
||
{
|
||
id: "fallback-2",
|
||
title: t('cards.raki.title'),
|
||
time: t('cards.raki.time'),
|
||
tag: "PER",
|
||
iconType: "glass",
|
||
colorTheme: "coral",
|
||
isFeatured: false,
|
||
},
|
||
{
|
||
id: "fallback-3",
|
||
title: t('cards.live_music.title'),
|
||
time: t('cards.live_music.time'),
|
||
tag: "★ ÖZEL",
|
||
iconType: "music",
|
||
colorTheme: "amber",
|
||
isFeatured: false,
|
||
}
|
||
];
|
||
|
||
const featuredEvent = defaultEvents.find(e => e.isFeatured) || defaultEvents[0];
|
||
const secondaryEvents = defaultEvents.filter(e => e.id !== featuredEvent.id);
|
||
|
||
// Tema renklerini almak için yardımcı fonksiyon
|
||
const getThemeColor = (theme: string) => {
|
||
switch (theme) {
|
||
case 'cyan': return '#00E5FF';
|
||
case 'coral': return '#FF5A36';
|
||
case 'amber': return '#FFA827';
|
||
default: return '#00E5FF';
|
||
}
|
||
};
|
||
|
||
const getThemeColors = (theme: string) => {
|
||
switch (theme) {
|
||
case 'cyan': return { main: '#00E5FF', bg: '#00E5FF1A', borderSpin: 'border-spin-cyan', darkBg: '#0C1828' };
|
||
case 'coral': return { main: '#FF5A36', bg: '#FF5A361A', borderSpin: 'border-spin-coral', darkBg: '#0C0808', hoverBg: '#110a0a' };
|
||
case 'amber': return { main: '#FFA827', bg: '#FFA8271A', borderSpin: 'border-spin-amber', darkBg: '#0C0A04', hoverBg: '#110d04' };
|
||
default: return { main: '#00E5FF', bg: '#00E5FF1A', borderSpin: 'border-spin-cyan', darkBg: '#0C1828', hoverBg: '#0C1828' };
|
||
}
|
||
};
|
||
|
||
// İkon bileşenini render etme yardımcı fonksiyonu
|
||
const renderIcon = (type: string, size: number = 22) => {
|
||
switch(type) {
|
||
case 'dj': return <Disc3 size={size} />;
|
||
case 'glass': return <WineGlassIcon />;
|
||
case 'music': return <Music size={size} />;
|
||
default: return <Music size={size} />;
|
||
}
|
||
};
|
||
|
||
return (
|
||
<section
|
||
id="events"
|
||
className="pt-28 pb-36 text-white relative overflow-hidden"
|
||
style={{ background: '#080D18' }}
|
||
>
|
||
{/* Aurora background blobs */}
|
||
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
||
<div
|
||
className="absolute top-[-20%] left-[-10%] w-[600px] h-[600px] rounded-full opacity-[0.12]"
|
||
style={{ background: 'radial-gradient(circle, #00E5FF 0%, transparent 70%)' }}
|
||
/>
|
||
<div
|
||
className="absolute bottom-[-10%] right-[-5%] w-[500px] h-[500px] rounded-full opacity-[0.1]"
|
||
style={{ background: 'radial-gradient(circle, #FF5A36 0%, transparent 70%)' }}
|
||
/>
|
||
<div
|
||
className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[700px] h-[700px] rounded-full opacity-[0.05]"
|
||
style={{ background: 'radial-gradient(circle, #FFA827 0%, transparent 70%)' }}
|
||
/>
|
||
</div>
|
||
|
||
{/* Fine dot grid */}
|
||
<div
|
||
className="absolute inset-0 opacity-[0.035] pointer-events-none"
|
||
style={{
|
||
backgroundImage: 'radial-gradient(circle, rgba(255,255,255,0.9) 1px, transparent 1px)',
|
||
backgroundSize: '36px 36px',
|
||
}}
|
||
/>
|
||
|
||
<div className="container mx-auto px-6 lg:px-12 relative z-10 space-y-10">
|
||
|
||
{/* ── Header ───────────────────────────────── */}
|
||
<div className="flex flex-col lg:flex-row lg:items-end justify-between gap-6">
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
>
|
||
<div className="flex items-center gap-2 mb-4">
|
||
<Zap size={13} className="text-coral" />
|
||
<span className="text-xs tracking-[0.4em] uppercase font-black text-coral">
|
||
Live Events
|
||
</span>
|
||
</div>
|
||
<h2 className="font-heading text-5xl md:text-6xl font-black leading-tight">
|
||
{t('title')}
|
||
</h2>
|
||
<p className="text-white/45 mt-3 text-base max-w-md leading-relaxed">
|
||
{t('description')}
|
||
</p>
|
||
</motion.div>
|
||
|
||
<motion.button
|
||
initial={{ opacity: 0, x: 20 }}
|
||
whileInView={{ opacity: 1, x: 0 }}
|
||
viewport={{ once: true }}
|
||
className="self-start lg:self-auto flex items-center gap-2 px-7 py-3.5 rounded-full font-black text-sm text-white flex-shrink-0 transition-all hover:scale-105"
|
||
style={{ background: 'linear-gradient(135deg, #FF5A36, #FFA827)' }}
|
||
>
|
||
<CalendarDays size={15} />
|
||
{t('view_calendar')}
|
||
</motion.button>
|
||
</div>
|
||
|
||
{/* ── Weekly schedule bar ───────────────────── */}
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 12 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
className="flex items-center gap-2 md:gap-4 bg-white/[0.04] rounded-2xl px-6 py-4 border border-white/[0.07]"
|
||
>
|
||
{WEEK_DAYS.map((day, i) => {
|
||
const accent = ACTIVE_DAYS[i];
|
||
const isActive = !!accent;
|
||
return (
|
||
<div key={day} className="flex-1 flex flex-col items-center gap-2">
|
||
<span
|
||
className={`text-[10px] font-black uppercase tracking-wider ${
|
||
isActive ? 'text-white' : 'text-white/25'
|
||
}`}
|
||
>
|
||
{day}
|
||
</span>
|
||
<div
|
||
className={`w-1.5 h-1.5 rounded-full transition-all ${isActive ? 'scale-125' : ''}`}
|
||
style={{
|
||
background: isActive ? accent : 'rgba(255,255,255,0.12)',
|
||
boxShadow: isActive ? `0 0 8px ${accent}` : 'none',
|
||
}}
|
||
/>
|
||
</div>
|
||
);
|
||
})}
|
||
</motion.div>
|
||
|
||
{/* ── Featured Event (Dinamic) ────────────── */}
|
||
{featuredEvent && (() => {
|
||
const c = getThemeColors(featuredEvent.colorTheme);
|
||
return (
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 24 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ duration: 0.7 }}
|
||
className={`${c.borderSpin} rounded-3xl p-px overflow-hidden`}
|
||
>
|
||
<div
|
||
className="rounded-3xl p-8 md:p-10 flex flex-col md:flex-row items-center gap-8 overflow-hidden relative"
|
||
style={{ background: c.darkBg }}
|
||
>
|
||
{/* Glow center */}
|
||
<div
|
||
className="absolute inset-0 pointer-events-none"
|
||
style={{
|
||
background: `radial-gradient(ellipse at 80% 50%, ${c.main}14 0%, transparent 60%)`,
|
||
}}
|
||
/>
|
||
|
||
{/* Left: text */}
|
||
<div className="flex-1 relative z-10">
|
||
<div className="flex items-center gap-3 mb-5">
|
||
<span
|
||
className="text-[10px] font-black uppercase tracking-[0.35em] px-3 py-1.5 rounded-full"
|
||
style={{ background: c.bg, color: c.main }}
|
||
>
|
||
{featuredEvent.tag}
|
||
</span>
|
||
<div className="flex items-center gap-1.5 text-white/40 text-xs">
|
||
<Calendar size={11} />
|
||
<span>{featuredEvent.time}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<h3 className="font-heading text-4xl md:text-5xl font-black mb-3 text-white leading-tight">
|
||
{featuredEvent.title}
|
||
</h3>
|
||
|
||
<div className="flex items-center gap-3 mb-7">
|
||
<div style={{ color: c.main }}>
|
||
{renderIcon(featuredEvent.iconType, 18)}
|
||
</div>
|
||
<span className="text-white/50 text-sm">Open-air · Beach Stage</span>
|
||
</div>
|
||
|
||
<EqBars color={c.main} />
|
||
</div>
|
||
|
||
{/* Right: vinyl */}
|
||
<div className="relative z-10 flex-shrink-0">
|
||
<VinylDisc />
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
);
|
||
})()}
|
||
|
||
{/* ── Secondary events: 2-col grid (Dinamic) ─────────── */}
|
||
{secondaryEvents.length > 0 && (
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
|
||
{secondaryEvents.map((evt, i) => {
|
||
const c = getThemeColors(evt.colorTheme);
|
||
return (
|
||
<motion.div
|
||
key={evt.id}
|
||
initial={{ opacity: 0, y: 20 }}
|
||
whileInView={{ opacity: 1, y: 0 }}
|
||
viewport={{ once: true }}
|
||
transition={{ delay: 0.1 * (i + 1) }}
|
||
className={`${c.borderSpin} rounded-3xl p-px group cursor-pointer`}
|
||
>
|
||
<div
|
||
className="rounded-3xl p-7 h-full flex flex-col gap-5 relative overflow-hidden transition-colors duration-300"
|
||
style={{ background: c.darkBg }}
|
||
>
|
||
<div
|
||
className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-400 pointer-events-none"
|
||
style={{
|
||
background: `radial-gradient(ellipse at 20% 50%, ${c.main}1A 0%, transparent 65%)`,
|
||
}}
|
||
/>
|
||
|
||
<div className="flex items-start justify-between">
|
||
<div
|
||
className="w-12 h-12 rounded-2xl flex items-center justify-center"
|
||
style={{ background: c.bg, color: c.main }}
|
||
>
|
||
{renderIcon(evt.iconType, 22)}
|
||
</div>
|
||
<span
|
||
className="text-[10px] font-black uppercase tracking-wider px-2.5 py-1 rounded-full"
|
||
style={{ background: c.bg, color: c.main }}
|
||
>
|
||
{evt.tag}
|
||
</span>
|
||
</div>
|
||
|
||
<div>
|
||
<h3 className="font-heading text-2xl font-black text-white mb-2">
|
||
{evt.title}
|
||
</h3>
|
||
<div className="flex items-center gap-1.5 text-sm text-white/40">
|
||
<Calendar size={12} />
|
||
<span>{evt.time}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="mt-auto opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||
<EqBars color={c.main} />
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
);
|
||
})}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* Wave → sand */}
|
||
<div className="absolute bottom-0 left-0 w-full leading-none pointer-events-none">
|
||
<svg viewBox="0 0 1440 64" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" className="w-full h-16">
|
||
<path d="M0,20 C360,64 720,0 1080,40 C1260,56 1380,10 1440,20 L1440,64 L0,64 Z" fill="#FFF8EE" />
|
||
</svg>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|