138 lines
5.4 KiB
TypeScript
138 lines
5.4 KiB
TypeScript
import { MapPin, Clock, ArrowRight, CalendarPlus } from "lucide-react";
|
|
import Link from "next/link";
|
|
|
|
interface MeetupData {
|
|
id?: string;
|
|
title: string;
|
|
date: string;
|
|
time?: string;
|
|
location?: string;
|
|
link?: string;
|
|
description?: string;
|
|
}
|
|
|
|
interface MeetupsSectionProps {
|
|
meetups: MeetupData[];
|
|
}
|
|
|
|
function formatMeetupDate(dateStr: string) {
|
|
const d = new Date(dateStr);
|
|
return {
|
|
month: d.toLocaleString("en-US", { month: "short" }).toUpperCase(),
|
|
day: String(d.getDate()),
|
|
full: d.toLocaleString("en-US", { weekday: "long", month: "long", day: "numeric", year: "numeric" }),
|
|
};
|
|
}
|
|
|
|
export function MeetupsSection({ meetups }: MeetupsSectionProps) {
|
|
return (
|
|
<section className="py-24 px-8 border-t border-zinc-800/50">
|
|
<div className="max-w-6xl mx-auto">
|
|
<div className="flex justify-between items-end mb-12">
|
|
<div>
|
|
<p className="uppercase tracking-[0.2em] text-primary mb-2 font-semibold text-xs">
|
|
Mark your calendar
|
|
</p>
|
|
<h2 className="text-3xl font-black tracking-tight">Upcoming Meetups</h2>
|
|
</div>
|
|
<div className="hidden md:flex items-center gap-4">
|
|
<a
|
|
href="/calendar.ics"
|
|
title="Subscribe to get all future meetups automatically"
|
|
className="flex items-center gap-1.5 text-xs text-on-surface-variant/60 hover:text-primary border border-zinc-700 hover:border-primary/50 rounded-lg px-3 py-1.5 transition-all"
|
|
>
|
|
<CalendarPlus size={14} />
|
|
Add to Calendar
|
|
</a>
|
|
<Link
|
|
href="/events"
|
|
className="flex items-center gap-2 text-sm text-primary font-semibold hover:gap-3 transition-all"
|
|
>
|
|
All events <ArrowRight size={16} />
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
|
|
{meetups.length === 0 ? (
|
|
<div className="border border-zinc-800 rounded-xl px-8 py-12 text-center">
|
|
<p className="text-on-surface-variant text-sm">
|
|
No upcoming meetups scheduled. Check back soon.
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
|
{meetups.map((meetup, i) => {
|
|
const { month, day, full } = formatMeetupDate(meetup.date);
|
|
const href = meetup.id ? `/events/${meetup.id}` : "#upcoming-meetups";
|
|
|
|
return (
|
|
<Link
|
|
key={meetup.id ?? i}
|
|
href={href}
|
|
className="group flex flex-col bg-zinc-900 border border-zinc-800 rounded-xl p-6 hover:border-zinc-700 hover:-translate-y-0.5 hover:shadow-xl transition-all duration-200"
|
|
>
|
|
<div className="flex items-start gap-4 mb-4">
|
|
<div className="bg-zinc-800 rounded-lg px-3 py-2 text-center shrink-0 min-w-[52px]">
|
|
<span className="block text-[10px] font-bold uppercase text-primary tracking-wider leading-none mb-0.5">
|
|
{month}
|
|
</span>
|
|
<span className="block text-2xl font-black leading-none">{day}</span>
|
|
</div>
|
|
<div className="min-w-0">
|
|
<h3 className="font-bold text-base leading-snug group-hover:text-primary transition-colors">
|
|
{meetup.title}
|
|
</h3>
|
|
<p className="text-on-surface-variant/60 text-xs mt-1">{full}</p>
|
|
</div>
|
|
</div>
|
|
|
|
{meetup.description && (
|
|
<p className="text-on-surface-variant text-sm leading-relaxed mb-4 flex-1 line-clamp-2">
|
|
{meetup.description}
|
|
</p>
|
|
)}
|
|
|
|
<div className="flex flex-col gap-1.5 mt-auto pt-4 border-t border-zinc-800/60">
|
|
{meetup.location && (
|
|
<p className="flex items-center gap-1.5 text-xs text-on-surface-variant/60">
|
|
<MapPin size={12} className="shrink-0 text-primary/60" />
|
|
{meetup.location}
|
|
</p>
|
|
)}
|
|
{meetup.time && (
|
|
<p className="flex items-center gap-1.5 text-xs text-on-surface-variant/60">
|
|
<Clock size={12} className="shrink-0 text-primary/60" />
|
|
{meetup.time}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
<span className="flex items-center gap-1.5 text-primary text-xs font-semibold mt-4 group-hover:gap-2.5 transition-all">
|
|
View Details <ArrowRight size={12} />
|
|
</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
|
|
<div className="md:hidden flex flex-col items-center gap-3 mt-8">
|
|
<Link
|
|
href="/events"
|
|
className="flex items-center gap-2 text-primary font-semibold text-sm"
|
|
>
|
|
All events <ArrowRight size={16} />
|
|
</Link>
|
|
<a
|
|
href="/calendar.ics"
|
|
className="flex items-center gap-1.5 text-xs text-on-surface-variant/60 hover:text-primary border border-zinc-700 hover:border-primary/50 rounded-lg px-3 py-1.5 transition-all"
|
|
>
|
|
<CalendarPlus size={14} />
|
|
Add to Calendar
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|