- Add organizer model/API, admin and public organizer pages, meetup cards - Refresh events/home/contact; add calendar dialog and carousel components - Optional Plausible via NEXT_PUBLIC_PLAUSIBLE_* env vars in root layout - Prisma migration, seed updates, baseline-and-migrate script Made-with: Cursor
83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { Navbar } from "@/components/public/Navbar";
|
|
import { HeroSection } from "@/components/public/HeroSection";
|
|
import { AboutSection } from "@/components/public/AboutSection";
|
|
import { CommunityLinksSection } from "@/components/public/CommunityLinksSection";
|
|
import { MeetupsSection } from "@/components/public/MeetupsSection";
|
|
import { FAQSection } from "@/components/public/FAQSection";
|
|
import { FinalCTASection } from "@/components/public/FinalCTASection";
|
|
import { Footer } from "@/components/public/Footer";
|
|
import { api } from "@/lib/api";
|
|
import { formatMeetupCivilDate, getMeetupStartUtc } from "@/lib/meetupEventTime";
|
|
|
|
export default function HomePage() {
|
|
const [meetup, setMeetup] = useState<any>(null);
|
|
const [allMeetups, setAllMeetups] = useState<any[]>([]);
|
|
const [settings, setSettings] = useState<Record<string, string>>({});
|
|
|
|
useEffect(() => {
|
|
api.getMeetups()
|
|
.then((data: any) => {
|
|
const all = Array.isArray(data) ? data : data?.meetups ?? [];
|
|
const now = new Date();
|
|
// Keep only PUBLISHED events with a future start (Brussels wall time → UTC), sorted closest-first
|
|
const upcoming = all
|
|
.filter((m: any) => {
|
|
if (m.status !== "PUBLISHED" || !m.date) return false;
|
|
const start = getMeetupStartUtc(m.date, m.time || "00:00");
|
|
return !Number.isNaN(start.getTime()) && start > now;
|
|
})
|
|
.sort(
|
|
(a: any, b: any) =>
|
|
getMeetupStartUtc(a.date, a.time || "00:00").getTime() -
|
|
getMeetupStartUtc(b.date, b.time || "00:00").getTime()
|
|
);
|
|
setAllMeetups(upcoming);
|
|
if (upcoming.length > 0) setMeetup(upcoming[0]);
|
|
})
|
|
.catch(() => {});
|
|
|
|
api.getPublicSettings()
|
|
.then((data) => setSettings(data))
|
|
.catch(() => {});
|
|
}, []);
|
|
|
|
const featuredCivil = meetup ? formatMeetupCivilDate(meetup.date) : null;
|
|
const meetupProps = meetup
|
|
? {
|
|
id: meetup.id,
|
|
month: featuredCivil
|
|
? featuredCivil.monthShort.charAt(0) + featuredCivil.monthShort.slice(1).toLowerCase()
|
|
: "TBD",
|
|
day: featuredCivil?.day ?? "--",
|
|
title: meetup.title,
|
|
location: meetup.location,
|
|
time: meetup.time,
|
|
link: meetup.link || "#meetup",
|
|
}
|
|
: undefined;
|
|
|
|
return (
|
|
<main>
|
|
<Navbar />
|
|
<section id="meetup">
|
|
<HeroSection meetup={meetupProps} />
|
|
</section>
|
|
<section id="about">
|
|
<AboutSection />
|
|
</section>
|
|
<CommunityLinksSection settings={settings} />
|
|
<section id="upcoming-meetups">
|
|
<MeetupsSection meetups={allMeetups} />
|
|
</section>
|
|
<section id="faq">
|
|
<FAQSection />
|
|
</section>
|
|
<FinalCTASection telegramLink={settings.telegram_link} />
|
|
<Footer />
|
|
</main>
|
|
);
|
|
}
|