Skip to content

BSL-38: add public events list page#32

Open
sanjanamanivannan wants to merge 1 commit intomainfrom
bsl-38-events-list
Open

BSL-38: add public events list page#32
sanjanamanivannan wants to merge 1 commit intomainfrom
bsl-38-events-list

Conversation

@sanjanamanivannan
Copy link
Copy Markdown
Contributor

created GET /api/events and temp events page (no ui) that connects the database.

image

Comment on lines +1 to +31
"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 ?? [];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
  );
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the entire file

Comment on lines +6 to +12
type Event = {
id: string;
title?: string | null;
name?: string | null;
startAt: string; // ISO string coming from JSON
location?: string | null;
};
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple things:
make title required
also remove name

so replace with this:

type Event = {
  id: string;
  title: string;
  startAt: string;
  location: string | null;
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines 5 to 13
export async function GET() {
const now = new Date();

const events = await prisma.event.findMany({
where: {
startAt: { gte: now },
},
orderBy: { startAt: "asc" },
});
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 }
    );
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants