feat: implement dynamic categories, admin category CRUD, fix routing and cleanup

This commit is contained in:
AyrisAI
2026-07-13 13:51:48 +03:00
parent 2f2dafcfb9
commit 5b44c78396
54 changed files with 3619 additions and 473 deletions
+119
View File
@@ -0,0 +1,119 @@
import { mockDb } from '@/lib/mockDb'
import { getTranslations, setRequestLocale } from 'next-intl/server'
import { Link } from '@/i18n/routing'
import Image from 'next/image'
import { Calendar, MapPin, Zap } from 'lucide-react'
interface Props {
params: Promise<{ locale: string }>
}
export default async function EventsPage({ params }: Props) {
const { locale } = await params
setRequestLocale(locale)
const t = await getTranslations('events')
// Get active events from DB
const events = await mockDb.getEvents(true)
return (
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 flex-1 space-y-8">
{/* Intro section */}
<div className="max-w-2xl space-y-2">
<h1 className="font-heading font-extrabold text-3xl sm:text-5xl text-pine lowercase">
{t('title')}
</h1>
<p className="text-ink/75 text-sm sm:text-base font-medium">
{t('subtitle')}
</p>
</div>
{/* Events Grid */}
{events.length === 0 ? (
<div className="bg-paper border border-pine/8 rounded-3xl p-12 text-center max-w-lg mx-auto">
<p className="text-shutter text-sm font-semibold">{t('noEvents')}</p>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{events.map((event) => {
const title = locale === 'tr' ? event.titleTr : locale === 'ru' ? event.titleRu : event.titleEn
const desc = locale === 'tr' ? event.descriptionTr : locale === 'ru' ? event.descriptionRu : event.descriptionEn
const venueName = event.listing ? event.listing.nameTr : ''
return (
<div
key={event.id}
className="bg-paper border border-pine/8 rounded-3xl overflow-hidden shadow-sm flex flex-col group hover:border-turquoise/30 hover:shadow-md transition duration-200"
>
{/* Image Cover */}
<div className="relative aspect-video bg-stone/40 overflow-hidden">
{event.coverImage ? (
<Image
src={event.coverImage}
alt={title}
fill
className="object-cover group-hover:scale-[1.02] transition duration-300"
/>
) : (
<div className="w-full h-full flex items-center justify-center text-shutter">
<Calendar className="w-8 h-8" />
</div>
)}
{/* Sponsored badge */}
{event.isSponsored && (
<span className="absolute top-4 left-4 inline-flex items-center gap-1 rounded-full bg-paper px-2.5 py-0.5 text-[9px] font-mono font-bold text-turquoise border border-turquoise/20 uppercase tracking-wider shadow-sm">
<Zap className="w-3 h-3 fill-turquoise text-turquoise" />
{t('sponsored')}
</span>
)}
</div>
{/* Event Details */}
<div className="p-6 flex-1 flex flex-col justify-between space-y-4">
<div className="space-y-2">
{/* Date badge */}
<div className="flex items-center gap-1.5 text-[11px] font-mono text-shutter font-bold uppercase tracking-wider">
<Calendar className="w-3.5 h-3.5" />
<span>
{new Date(event.startDate).toLocaleDateString(locale, {
day: 'numeric',
month: 'long',
hour: '2-digit',
minute: '2-digit'
})}
</span>
</div>
<h3 className="font-heading font-extrabold text-lg text-pine lowercase group-hover:text-turquoise transition-colors leading-tight">
{title}
</h3>
<p className="text-ink/70 text-xs leading-relaxed font-medium line-clamp-3">
{desc}
</p>
</div>
{/* Hosting Venue info */}
{event.listing && (
<div className="border-t border-dashed border-pine/8 pt-4 flex items-center justify-between text-xs">
<div className="flex items-center gap-1 text-ink/75 font-semibold">
<MapPin className="w-3.5 h-3.5 text-shutter/65" />
<span className="lowercase text-shutter">{t('venue')}:</span>
<Link
href={`/${event.listing.category?.slug || 'isletme'}/${event.listing.slug}`}
className="text-pine hover:text-turquoise transition-colors"
>
{venueName}
</Link>
</div>
</div>
)}
</div>
</div>
)
})}
</div>
)}
</main>
)
}