Conversation
| "use client"; | ||
|
|
||
| import { useEffect, useState } from "react"; | ||
| import PublicLayout from "@/components/layout/PublicLayout"; | ||
|
|
||
| type Event = { | ||
| id: string; | ||
| title?: string | null; | ||
| name?: string | null; | ||
| startAt: string; // ISO string coming from JSON | ||
| location?: string | null; | ||
| }; | ||
|
|
||
| export default function EventsPage() { | ||
| const [events, setEvents] = useState<Event[] | null>(null); | ||
|
|
||
| useEffect(() => { | ||
| async function load() { | ||
| const res = await fetch("/api/events"); | ||
| if (!res.ok) { | ||
| setEvents([]); | ||
| return; | ||
| } | ||
| const data = (await res.json()) as Event[]; | ||
| setEvents(data); | ||
| } | ||
| load(); | ||
| }, []); | ||
|
|
||
| const items = events ?? []; | ||
|
|
There was a problem hiding this comment.
the page does not need to be a client component. Convert it to an async server component and query prisma directory... remove use client, useEffect, and useState entirely.
The client approach causes a loading flash on every visit. Server components are the default and right tool here
There was a problem hiding this comment.
So convert to a server component..
AI generated this, looks to be good but verify/test please:
import PublicLayout from "@/components/layout/PublicLayout";
import { prisma } from "@/lib/prisma";
export default async function EventsPage() {
let events: { id: string; title: string; startAt: Date; location: string | null }[] = [];
try {
events = await prisma.event.findMany({
where: { startAt: { gte: new Date() } },
orderBy: { startAt: "asc" },
select: { id: true, title: true, startAt: true, location: true },
});
} catch (err) {
console.error("[EventsPage]", err);
// events stays [] — page renders gracefully with empty state
}
return (
<PublicLayout>
<div className="mx-auto max-w-4xl px-6 py-12">
<h1 className="text-3xl font-semibold">Events</h1>
<p className="mt-2 text-gray-600">Upcoming events</p>
{events.length === 0 ? (
<div className="mt-8 rounded-lg border p-6 text-gray-600">
No upcoming events right now. Check back soon.
</div>
) : (
<ul className="mt-8 space-y-4">
{events.map((event) => (
<li key={event.id} className="rounded-lg border p-4">
<div className="font-medium">{event.title}</div>
<div className="mt-1 text-sm text-gray-600">
{event.startAt.toLocaleDateString("en-US", {
weekday: "short", month: "short", day: "numeric", year: "numeric",
})}
{event.location ? ` • ${event.location}` : ""}
</div>
</li>
))}
</ul>
)}
</div>
</PublicLayout>
);
}
There was a problem hiding this comment.
this is the entire file
| type Event = { | ||
| id: string; | ||
| title?: string | null; | ||
| name?: string | null; | ||
| startAt: string; // ISO string coming from JSON | ||
| location?: string | null; | ||
| }; |
There was a problem hiding this comment.
Couple things:
make title required
also remove name
so replace with this:
type Event = {
id: string;
title: string;
startAt: string;
location: string | null;
};
There was a problem hiding this comment.
actually if you resolve the client to server component issue, this wouldn't matter since it would disappear since the server component queries prisma directly and typescript infers the types from the schema auromatically, so no type Event ={} needed
| export async function GET() { | ||
| const now = new Date(); | ||
|
|
||
| const events = await prisma.event.findMany({ | ||
| where: { | ||
| startAt: { gte: now }, | ||
| }, | ||
| orderBy: { startAt: "asc" }, | ||
| }); |
There was a problem hiding this comment.
Add a try/catch.. if Prisma throws for any reason, the route crashes with an unhandled promise rejection and returns a generic 500 with nothing useful
Something like this:
export async function GET() {
try {
const now = new Date();
const events = await prisma.event.findMany({
where: { startAt: { gte: now } },
orderBy: { startAt: "asc" },
});
return NextResponse.json(events);
} catch (err) {
console.error("[GET /api/events]", err);
return NextResponse.json(
{ error: "Failed to load events" },
{ status: 500 }
);
}
}
created GET /api/events and temp events page (no ui) that connects the database.