mirror of
https://github.com/arthur-pbty/binouz.git
synced 2026-06-03 15:07:17 +02:00
b7010a1704
- Implemented AuthButton component for Discord sign-in and sign-out functionality. - Created CopyButton component for copying server IP addresses. - Developed EventCard and GradeCard components for displaying events and grades. - Added Footer and Navbar components for site navigation and information. - Introduced PurchaseButton for handling grade purchases with Stripe integration. - Created SectionHeader component for consistent section titles. - Implemented session management with SessionProvider for NextAuth. - Set up PostgreSQL database with Docker and Prisma for data management. - Added admin guard functionality to restrict access to certain routes. - Configured NextAuth with Discord provider for user authentication. - Defined Prisma schema for user, admin, grade, event, and purchase models. - Seeded database with initial grades and events data. - Added SVG hero image for the landing page. - Extended NextAuth types to include additional user properties.
162 lines
5.7 KiB
TypeScript
162 lines
5.7 KiB
TypeScript
import EventCard from "@/components/event-card";
|
|
import Footer from "@/components/footer";
|
|
import GradeCard from "@/components/grade-card";
|
|
import Hero from "@/components/hero";
|
|
import Navbar from "@/components/navbar";
|
|
import SectionHeader from "@/components/section-header";
|
|
import { db } from "@/lib/db";
|
|
import { fallbackEvents, fallbackGrades, siteConfig } from "@/lib/site";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
type Grade = {
|
|
id: string;
|
|
name: string;
|
|
price: number;
|
|
description: string;
|
|
};
|
|
|
|
type Event = {
|
|
id: string;
|
|
title: string;
|
|
description: string;
|
|
eventDate: Date;
|
|
};
|
|
|
|
const getGrades = async (): Promise<Grade[]> => {
|
|
try {
|
|
const grades = await db.grade.findMany({ orderBy: { price: "asc" } });
|
|
return grades.length > 0 ? grades : fallbackGrades;
|
|
} catch {
|
|
return fallbackGrades;
|
|
}
|
|
};
|
|
|
|
const getEvents = async (): Promise<Event[]> => {
|
|
try {
|
|
const events = await db.event.findMany({ orderBy: { eventDate: "asc" } });
|
|
return events.length > 0 ? events : fallbackEvents;
|
|
} catch {
|
|
return fallbackEvents;
|
|
}
|
|
};
|
|
|
|
export default async function Home() {
|
|
const [grades, events] = await Promise.all([getGrades(), getEvents()]);
|
|
|
|
return (
|
|
<div className="flex min-h-screen flex-col">
|
|
<Navbar />
|
|
<main className="flex-1">
|
|
<Hero />
|
|
|
|
<section id="presentation" className="py-20">
|
|
<div className="mx-auto w-full max-w-6xl px-6">
|
|
<SectionHeader
|
|
eyebrow="Presentation"
|
|
title="UHC designed for competitive squads"
|
|
description="Balance, clarity, and tension. BinouzUHC delivers a premium UHC loop with PvP-first tuning."
|
|
/>
|
|
<div className="grid gap-6 md:grid-cols-3">
|
|
{[
|
|
{
|
|
title: "Precision combat",
|
|
description:
|
|
"Hits register fast, clean hitboxes, and optimized knockback for UHC duels.",
|
|
},
|
|
{
|
|
title: "Event experience",
|
|
description:
|
|
"Weekly tournaments, bracket nights, and events built for squads.",
|
|
},
|
|
{
|
|
title: "Community core",
|
|
description:
|
|
"Active moderation, high signal Discord, and competitive rankings.",
|
|
},
|
|
].map((item) => (
|
|
<div
|
|
key={item.title}
|
|
className="rounded-3xl border border-white/10 bg-white/5 p-6 text-sm text-slate-300 backdrop-blur"
|
|
>
|
|
<p className="text-xs uppercase tracking-[0.3em] text-cyan-200/80">
|
|
{item.title}
|
|
</p>
|
|
<p className="mt-4 text-base text-white">{item.description}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="grades" className="py-20">
|
|
<div className="mx-auto w-full max-w-6xl px-6">
|
|
<SectionHeader
|
|
eyebrow="Boutique"
|
|
title="Grades premium"
|
|
description="Unlock cosmetics, priority access, and competitive advantages."
|
|
/>
|
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
{grades.map((grade) => (
|
|
<GradeCard key={grade.id} grade={grade} />
|
|
))}
|
|
</div>
|
|
<p className="mt-6 text-xs uppercase tracking-[0.3em] text-slate-500">
|
|
{siteConfig.shopDisclaimer}
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="events" className="py-20">
|
|
<div className="mx-auto w-full max-w-6xl px-6">
|
|
<SectionHeader
|
|
eyebrow="Events"
|
|
title="Upcoming challenges"
|
|
description="Competitive formats tailored for UHC players and squad leaders."
|
|
/>
|
|
<div className="grid gap-6 md:grid-cols-2">
|
|
{events.map((event) => (
|
|
<EventCard key={event.id} event={event} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="discord" className="py-20">
|
|
<div className="mx-auto w-full max-w-6xl px-6">
|
|
<div className="rounded-3xl border border-white/10 bg-linear-to-r from-violet-600/20 via-indigo-500/20 to-cyan-400/20 p-10 text-center backdrop-blur">
|
|
<p className="text-xs uppercase tracking-[0.4em] text-cyan-200/80">
|
|
Discord
|
|
</p>
|
|
<h2 className="mt-4 text-3xl font-semibold text-white">
|
|
Join the BinouzUHC squad
|
|
</h2>
|
|
<p className="mt-3 text-sm text-slate-300">
|
|
News, tournaments, and admin contact. Stay synced with the
|
|
community.
|
|
</p>
|
|
<div className="mt-6 flex flex-wrap justify-center gap-4">
|
|
<a
|
|
href={siteConfig.discordInviteUrl}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="inline-flex items-center justify-center rounded-full border border-white/15 bg-white/10 px-6 py-3 text-xs font-semibold uppercase tracking-[0.3em] text-white transition hover:bg-white/20"
|
|
>
|
|
Connect Discord
|
|
</a>
|
|
<a
|
|
href="#presentation"
|
|
className="inline-flex items-center justify-center rounded-full border border-white/15 px-6 py-3 text-xs font-semibold uppercase tracking-[0.3em] text-white/80 transition hover:text-white"
|
|
>
|
|
Learn more
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
<Footer />
|
|
</div>
|
|
);
|
|
}
|