Skip to content

implement api routes, server actions and caching, and the event detai…#3

Merged
Preiz68 merged 1 commit into
mainfrom
api-routes
Dec 22, 2025
Merged

implement api routes, server actions and caching, and the event detai…#3
Preiz68 merged 1 commit into
mainfrom
api-routes

Conversation

@Preiz68
Copy link
Copy Markdown
Owner

@Preiz68 Preiz68 commented Dec 22, 2025

…ls page

Summary by CodeRabbit

  • New Features

    • Event detail pages now display comprehensive information including dates, times, locations, and agendas.
    • Users can book events through a new booking interface.
    • Similar event recommendations appear on event detail pages.
  • Chores

    • Updated project dependencies and bumped version to 1.0.0.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 22, 2025

Walkthrough

This PR implements a complete event management system with API routes for creating events (with Cloudinary image uploads) and fetching individual or multiple events, new pages for listing and displaying event details, updated data models with normalized date/time fields and slug generation, server-side data fetching, and related UI components for event cards and booking.

Changes

Cohort / File(s) Summary
API Routes for Events
app/api/events/route.ts, app/api/events/[slug]/route.ts
Adds POST handler to parse multipart form data, validate/upload images to Cloudinary, and save events to MongoDB; adds GET handlers to retrieve events by slug or list all events sorted by creation date; includes structured error handling and 404 responses.
Event Pages
app/page.tsx, app/event/[slug]/page.tsx
Converts homepage to async component fetching events via API with Next.js caching; adds dynamic event details page that resolves slug params and renders EventDetails component with Suspense fallback.
Event Components
components/EventCard.tsx, components/EventDetails.tsx, components/BookEvent.tsx
Updates EventCard link to use dynamic slug-based routing; introduces EventDetails server component that fetches event data, handles 404s, loads similar events, and renders detailed event layout with agenda, tags, booking sidebar; adds BookEvent form component for email capture.
Database Models
database/event.model.ts, database/booking.model.ts
Extends IEvent with Mongoose Document; replaces startAt with separate date and time string fields; updates slug generation via pre-save hook; simplifies validators for tags and agenda; removes comment block from booking model.
Server Actions
lib/actions/event.actions.ts
Adds getSimilarEventBySlug function to query and return up to three events sharing tags with a given event, excluding the original event.
Configuration & Dependencies
next.config.ts, package.json
Enables cacheComponents and restricts remote images to Cloudinary; bumps version to 1.0.0; updates Next.js to 16.1.0, React to 18.3.1, Mongoose to ^9.0.2; adds tailwindcss, postcss, tailwind-merge, tw-animate-css.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Browser
    participant Page as Next.js Page
    participant API as API Routes
    participant DB as MongoDB
    participant CDN as Cloudinary
    participant Action as Server Action

    rect rgba(100, 150, 200, 0.1)
    note over Client,Action: Flow 1: Browse Events
    Client->>Page: Visit /
    Page->>API: GET /api/events
    API->>DB: Query events sorted by creation date desc
    DB-->>API: Return events list
    API-->>Page: 200 {events}
    Page-->>Client: Render EventCard list
    end

    rect rgba(100, 180, 150, 0.1)
    note over Client,Action: Flow 2: View Event Details
    Client->>Page: Click EventCard → /event/[slug]
    Page->>Page: Resolve slug from params
    Page->>API: GET /api/events/[slug]
    API->>DB: Query event by slug
    DB-->>API: Return event document
    API-->>Page: 200 {event}
    par Fetch Similar Events
        Page->>Action: getSimilarEventBySlug(slug)
        Action->>DB: Find event by slug
        Action->>DB: Query events with matching tags
        DB-->>Action: Return similar events (max 3)
    and Render Page
        Page-->>Client: Render EventDetails with event
    end
    Action-->>Page: Similar events
    Page->>Page: Append similar events to UI
    Client-->>Client: Display event details, agenda, tags, booking
    end

    rect rgba(200, 150, 100, 0.1)
    note over Client,Action: Flow 3: Create New Event
    Client->>Page: Fill form with event data + image
    Page->>API: POST /api/events (multipart/form-data)
    API->>CDN: Upload image via upload_stream
    CDN-->>API: Return secure_url
    API->>DB: Create event document with image URL
    DB-->>API: Return saved event
    API-->>Page: 201 {event}
    Page-->>Client: Confirm success
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

  • Database schema changes: Review date/time normalization logic, slug generation pre-save hook, and validator updates in database/event.model.ts
  • Image upload pipeline: Verify multipart form parsing, Cloudinary upload stream handling, and error cases in app/api/events/route.ts
  • API error handling: Ensure consistent error responses and database configuration checks (MONGODB_URI) across both API routes
  • Server-side data fetching: Validate Next.js caching directives and cache invalidation patterns in app/page.tsx and components/EventDetails.tsx
  • Similar events logic: Check the query filter in getSimilarEventBySlug to ensure correct tag matching and exclusion of the source event

Poem

🐰 A warren of events now takes its place,
With images soaring to the cloud so high,
Tags dance in similar grace, as slugs pave the way,
Booking bells ring soft, time fields shine bright,
From hop to click—details unfold with delight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: API routes (events GET/POST and single event GET), server actions (getSimilarEventBySlug), and caching (cache hints in page.tsx and next.config.ts), plus the event details page.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch api-routes

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 22, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #4

coderabbitai Bot added a commit that referenced this pull request Dec 22, 2025
Docstrings generation was requested by @Preiz68.

* #3 (comment)

The following files were modified:

* `app/api/events/[slug]/route.ts`
* `app/api/events/route.ts`
* `database/event.model.ts`
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (12)
components/BookEvent.tsx (1)

25-31: Add form validation and required attribute.

The email input lacks a required attribute and client-side validation beyond the basic HTML5 type check. Consider adding more robust validation.

🔎 Proposed improvements
  <input
    type="email"
+   required
    value={email}
    onChange={(e) => setEmail(e.target.value)}
    id="email"
    placeholder="Enter your email address"
  />
package.json (2)

8-8: Lint script removed.

The removal of the lint script could impact code quality checks in CI/CD pipelines. Ensure linting is still performed through other means.


19-20: Consider documenting the Router strategy if using React 18 with Next.js 16.

React 18.3.1 is compatible with Next.js 16, but Next.js 16 leverages React 19.2. If this project uses the App Router, upgrading to React 19 is recommended to access the latest features. If using the Pages Router, React 18.3.1 is supported and appropriate.

lib/actions/event.actions.ts (1)

13-32: Consider adding sort order for consistent results.

The similar events query doesn't specify a sort order, which may return results in an unpredictable sequence. Consider sorting by date or creation time for consistency.

🔎 Proposed enhancement
  return Event.find(
    {
      _id: { $ne: event._id },
      tags: { $in: event.tags },
    },
    {
      _id: 0,
      title: 1,
      slug: 1,
      description: 1,
      image: 1,
      date: 1,
      time: 1,
      location: 1,
      mode: 1,
      tags: 1,
    }
  )
+   .sort({ date: 1 })
    .limit(3)
    .lean();
components/EventDetails.tsx (2)

29-32: Potential key collision with duplicate agenda items.

Using the item value directly as the key can cause React reconciliation issues if there are duplicate agenda items.

🔎 Proposed fix
     <ul>
-      {agendaItems.map((item) => (
-        <li key={item}>{item}</li>
+      {agendaItems.map((item, index) => (
+        <li key={`${item}-${index}`}>{item}</li>
       ))}
     </ul>

38-44: Potential key collision with duplicate tags.

Same issue as agenda items—using tag directly as a key may cause issues if duplicate tags exist.

🔎 Proposed fix
   <div className="flex flex-row gap-1.5 flex-wrap">
-    {tags.map((tag) => (
-      <div className="pill" key={tag}>
+    {tags.map((tag, index) => (
+      <div className="pill" key={`${tag}-${index}`}>
         {tag}
       </div>
     ))}
   </div>
app/api/events/route.ts (3)

14-21: Unnecessary try-catch—Object.fromEntries won't throw for valid FormData.

Object.fromEntries(formData.entries()) does not parse JSON; it converts FormData entries to an object. The error message "Invalid JSON data format" is misleading, and this try-catch block is effectively dead code.

🔎 Proposed fix
     const formData = await req.formData();
-
-    let event;
-
-    try {
-      event = Object.fromEntries(formData.entries());
-    } catch (error) {
-      return NextResponse.json(
-        { message: "Invalid JSON data format" },
-        { status: 400 }
-      );
-    }
+    const event = Object.fromEntries(formData.entries());

6-69: No input validation for required event fields.

The POST handler validates only the image file presence but doesn't validate other required fields (title, description, etc.) before attempting to create the event. Mongoose validation will catch these, but earlier validation provides better error messages and avoids unnecessary Cloudinary uploads for invalid requests.


37-47: Validate Cloudinary credentials before upload.

The code uses cloudinary.uploader.upload_stream() without verifying that CLOUDINARY_URL or individual credentials (CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET) are configured. Missing or incorrect credentials will fail with an unclear "Unknown API key" error. Add validation at the start of the POST handler to ensure Cloudinary is properly configured, providing a clear error message if it's not.

database/event.model.ts (3)

184-185: Commented-out unique index is redundant.

The slug field already has unique: true at line 33, which creates an implicit unique index. The commented-out explicit index declaration is unnecessary and can be removed.


3-21: IEvent extends Document includes conflicting type definitions.

When extending Document, Mongoose already provides _id, createdAt, updatedAt, etc. Explicitly defining createdAt and updatedAt in your interface may cause type conflicts. Consider either omitting them from your interface or using a separate interface for the input data.

🔎 Alternative approach
// Input interface (without Document fields)
export interface IEventInput {
  title: string;
  slug?: string;
  description: string;
  // ... other fields without createdAt/updatedAt
}

// Full document interface
export interface IEvent extends IEventInput, Document {
  createdAt: Date;
  updatedAt: Date;
}

112-130: Pre-save hook should use next() callback for proper error handling.

The pre-save hook doesn't call next(), which is required for Mongoose middleware. While modern Mongoose may handle this, explicitly calling next() or returning a Promise ensures proper middleware chain execution and error propagation.

🔎 Proposed fix
-EventSchema.pre("save", function () {
+EventSchema.pre("save", function (next) {
+  try {
     const event = this as IEvent;
 
     // Generate slug only if title changed or document is new
     if (event.isModified("title") || event.isNew) {
       event.slug = generateSlug(event.title);
     }
 
     // Normalize date to ISO format if it's not already
     if (event.isModified("date")) {
       event.date = normalizeDate(event.date);
     }
 
     // Normalize time format (HH:MM)
     if (event.isModified("time")) {
       event.time = normalizeTime(event.time);
     }
+    next();
+  } catch (error) {
+    next(error as Error);
+  }
 });
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f0b55d and 756cf7d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • app/api/events/[slug]/route.ts
  • app/api/events/route.ts
  • app/event/[slug]/page.tsx
  • app/page.tsx
  • components/BookEvent.tsx
  • components/EventCard.tsx
  • components/EventDetails.tsx
  • database/booking.model.ts
  • database/event.model.ts
  • lib/actions/event.actions.ts
  • next.config.ts
  • package.json
💤 Files with no reviewable changes (1)
  • database/booking.model.ts
🧰 Additional context used
🧬 Code graph analysis (4)
app/api/events/route.ts (2)
app/api/events/[slug]/route.ts (1)
  • GET (17-86)
lib/constants.ts (1)
  • events (1-98)
database/event.model.ts (1)
database/index.ts (1)
  • IEvent (12-12)
app/api/events/[slug]/route.ts (2)
app/api/events/route.ts (1)
  • GET (72-94)
database/event.model.ts (1)
  • IEvent (4-21)
app/page.tsx (2)
lib/constants.ts (1)
  • events (1-98)
database/event.model.ts (1)
  • IEvent (4-21)
🔇 Additional comments (9)
next.config.ts (2)

5-12: LGTM! Cloudinary image configuration is correct.

The remote patterns configuration properly restricts image sources to Cloudinary's CDN, which aligns with the image upload functionality in the API routes.


4-4: cacheComponents is a valid Next.js 16.1.0 configuration option. The cacheComponents flag is a feature in Next.js 16.1.0 that is enabled by setting it to true in your next.config.ts file. No changes needed.

components/EventCard.tsx (1)

28-28: LGTM! Route updated to match dynamic slug pattern.

The href correctly uses the dynamic route /event/${slug} pattern that aligns with the new app/event/[slug]/page.tsx structure introduced in this PR.

lib/actions/event.actions.ts (1)

34-34: LGTM! Error handling returns safe fallback.

Returning an empty array on error prevents the UI from breaking and provides graceful degradation.

app/event/[slug]/page.tsx (1)

13-13: LGTM! Suspense boundary provides good UX.

The Suspense wrapper with a loading fallback provides a good user experience while the EventDetails component fetches data.

app/page.tsx (1)

28-34: LGTM! Defensive rendering logic.

The conditional checks for events existence and length before mapping provide good defensive programming and prevent runtime errors.

app/api/events/[slug]/route.ts (2)

17-56: LGTM! Well-structured API route with proper validation.

This implementation demonstrates best practices:

  • Correct async/await for Next.js 16 params
  • Comprehensive slug validation and sanitization
  • Appropriate error responses (400, 404, 500)
  • Type-safe with RouteParams and IEvent
  • lean() query for performance

57-85: Excellent error handling with environment-aware logging.

The error handling covers specific cases (MONGODB_URI configuration errors) and uses development-only console logging, which is a good security practice.

components/EventDetails.tsx (1)

9-9: Request verification for BASE_URL availability at runtime.

NEXT_PUBLIC_BASE_URL is read at module initialization time. Ensure this environment variable is set correctly in all deployment environments. If undefined, the fetch at line 52 will fail with an invalid URL.

Comment thread app/api/events/route.ts
Comment on lines +31 to +32
let tags = JSON.parse(formData.get("tags") as string);
let agenda = JSON.parse(formData.get("agenda") as string);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Unhandled JSON.parse errors will crash the request.

If tags or agenda fields contain invalid JSON or are missing, JSON.parse will throw an uncaught exception, resulting in a 500 error instead of a proper 400 validation error.

🔎 Proposed fix
-    let tags = JSON.parse(formData.get("tags") as string);
-    let agenda = JSON.parse(formData.get("agenda") as string);
+    let tags: string[];
+    let agenda: string[];
+    
+    try {
+      const tagsRaw = formData.get("tags");
+      const agendaRaw = formData.get("agenda");
+      
+      if (!tagsRaw || !agendaRaw) {
+        return NextResponse.json(
+          { message: "Tags and agenda are required" },
+          { status: 400 }
+        );
+      }
+      
+      tags = JSON.parse(tagsRaw as string);
+      agenda = JSON.parse(agendaRaw as string);
+    } catch {
+      return NextResponse.json(
+        { message: "Invalid JSON format for tags or agenda" },
+        { status: 400 }
+      );
+    }
🤖 Prompt for AI Agents
In app/api/events/route.ts around lines 31-32, the direct calls to JSON.parse on
formData.get("tags") and formData.get("agenda") can throw if the fields are
missing or contain invalid JSON; wrap each parse in validation and error
handling: first check formData.get(...) is not null/undefined and is a string,
then parse inside a try/catch and on parse errors return a 400 Bad Request with
a clear validation message; alternatively, use a helper that attempts JSON.parse
and returns a tuple or throws a controlled ValidationError so callers can
produce a 400 response rather than letting the exception bubble as a 500.

Comment thread app/api/events/route.ts
Comment on lines +77 to +78
if (!events)
return NextResponse.json({ message: "No event found" }, { status: 404 });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Incorrect empty check—Event.find() returns an array, never null.

Event.find() returns an empty array [] when no documents match, not null or undefined. The condition !events will never be true.

🔎 Proposed fix
     const events = await Event.find().sort({ createdAt: -1 });
 
-    if (!events)
+    if (events.length === 0)
       return NextResponse.json({ message: "No event found" }, { status: 404 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!events)
return NextResponse.json({ message: "No event found" }, { status: 404 });
const events = await Event.find().sort({ createdAt: -1 });
if (events.length === 0)
return NextResponse.json({ message: "No event found" }, { status: 404 });
🤖 Prompt for AI Agents
In app/api/events/route.ts around lines 77 to 78, the code checks `if (!events)`
after `Event.find()` but `find()` always returns an array, so that condition
never triggers; replace the check with an explicit empty-array check such as `if
(events.length === 0)` (or `if (!events?.length)` if you prefer defensive
coding) and return the 404 response when the array is empty.

Comment thread app/event/[slug]/page.tsx
}: {
params: Promise<{ slug: string }>;
}) => {
const slug = params.then((p) => p.slug);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

CRITICAL: Promise assigned instead of awaited value.

Line 9 assigns a Promise to the slug variable instead of extracting the actual string value. This will cause runtime errors when EventDetails tries to use the slug.

🔎 Required fix
  const EventDetailsPage = async ({
    params,
  }: {
    params: Promise<{ slug: string }>;
  }) => {
-   const slug = params.then((p) => p.slug);
+   const { slug } = await params;

    return (
      <main>
        <Suspense fallback={<div>Loading...</div>}>
-         <EventDetails params={slug} />
+         <EventDetails slug={slug} />
        </Suspense>
      </main>
    );
  };
🤖 Prompt for AI Agents
In app/event/[slug]/page.tsx around line 9, the code assigns a Promise to `slug`
(const slug = params.then((p) => p.slug)); instead of the resolved string;
change this to await the params and extract the slug (e.g., ensure the
component/function is async, do `const { slug } = await params;` or `const
paramsResolved = await params; const slug = paramsResolved.slug;`) and ensure
`slug` is typed as a string before passing to EventDetails.

Comment thread app/page.tsx
Comment on lines +6 to +12
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;

const Page = async () => {
"use cache";
cacheLife("hours");
const response = await fetch(`${BASE_URL}/api/events`);
const { events } = await response.json();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error handling and validation for data fetching.

The fetch call lacks critical error handling:

  1. BASE_URL might be undefined if the environment variable is not set
  2. No check for response.ok before parsing JSON
  3. No try-catch for network or parsing errors

This will cause unhandled exceptions if the API is unavailable or returns an error.

🔎 Proposed fix
  const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;

  const Page = async () => {
    "use cache";
    cacheLife("hours");
+   
+   if (!BASE_URL) {
+     console.error('NEXT_PUBLIC_BASE_URL is not configured');
+     return <div>Configuration error</div>;
+   }
+   
+   try {
      const response = await fetch(`${BASE_URL}/api/events`);
+     
+     if (!response.ok) {
+       throw new Error(`Failed to fetch events: ${response.statusText}`);
+     }
+     
      const { events } = await response.json();
+     
+     if (!events || !Array.isArray(events)) {
+       throw new Error('Invalid events data structure');
+     }
+   } catch (error) {
+     console.error('Error fetching events:', error);
+     return <div>Failed to load events</div>;
+   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
const Page = async () => {
"use cache";
cacheLife("hours");
const response = await fetch(`${BASE_URL}/api/events`);
const { events } = await response.json();
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
const Page = async () => {
"use cache";
cacheLife("hours");
if (!BASE_URL) {
console.error('NEXT_PUBLIC_BASE_URL is not configured');
return <div>Configuration error</div>;
}
try {
const response = await fetch(`${BASE_URL}/api/events`);
if (!response.ok) {
throw new Error(`Failed to fetch events: ${response.statusText}`);
}
const { events } = await response.json();
if (!events || !Array.isArray(events)) {
throw new Error('Invalid events data structure');
}
} catch (error) {
console.error('Error fetching events:', error);
return <div>Failed to load events</div>;
}
🤖 Prompt for AI Agents
In app/page.tsx around lines 6 to 12, the fetch logic doesn't validate BASE_URL,
doesn't check response.ok, and lacks try/catch for network or parsing errors;
update the code to (1) validate that process.env.NEXT_PUBLIC_BASE_URL is set and
throw or return a clear error if missing, (2) wrap the fetch and response.json()
in a try/catch to handle network and parsing failures, (3) after fetch check
response.ok and handle non-2xx responses by logging/throwing a descriptive error
(including status and statusText) instead of blindly calling response.json(),
and (4) return a safe default (e.g., empty events array) or propagate a
controlled error to the caller.

Comment thread components/BookEvent.tsx
Comment on lines +9 to +15
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();

setTimeout(() => {
setSubmitted(true);
}, 1000);
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Form submission is only simulated, not implemented.

The handleSubmit function uses a timeout to simulate submission but never actually sends the email data to any API endpoint. This creates a misleading user experience where users think they've registered but no data is persisted.

Do you want me to generate an implementation that calls an API endpoint to persist the booking data?

🔎 Proposed fix to add actual API call
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
-
-    setTimeout(() => {
-      setSubmitted(true);
-    }, 1000);
+    
+    // Call API to submit booking
+    fetch('/api/bookings', {
+      method: 'POST',
+      headers: { 'Content-Type': 'application/json' },
+      body: JSON.stringify({ email }),
+    })
+      .then(response => {
+        if (response.ok) {
+          setSubmitted(true);
+        }
+      })
+      .catch(error => {
+        console.error('Booking failed:', error);
+        // Handle error state
+      });
  };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setTimeout(() => {
setSubmitted(true);
}, 1000);
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
// Call API to submit booking
fetch('/api/bookings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
})
.then(response => {
if (response.ok) {
setSubmitted(true);
}
})
.catch(error => {
console.error('Booking failed:', error);
// Handle error state
});
};
🤖 Prompt for AI Agents
In components/BookEvent.tsx around lines 9 to 15, the handleSubmit currently
only uses setTimeout to simulate submission and does not persist any booking
data; replace the simulated timeout with an actual async submission: collect the
form state (name, email, event details) and send it to your backend booking API
(via fetch or axios) inside an async function, await the response, and on
success setSubmitted(true) and clear loading state; wrap the call in try/catch
to set an error state on failure and avoid marking submitted when the request
fails; also remove the artificial setTimeout and add a loading indicator state
so the UI reflects real network activity.


if (!event || !event.description) notFound();

const bookings = 10;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hardcoded bookings count is misleading.

The bookings value is hardcoded to 10, which will display misleading information to users ("Join 10 people who have already booked"). This should either fetch real booking data or be removed until the booking feature is implemented.

🤖 Prompt for AI Agents
In components/EventDetails.tsx around line 61 the bookings variable is hardcoded
to 10 which shows misleading booking counts; remove the hardcoded value and
either fetch the real booking count from the event/booking API/props or, if the
feature isn't implemented, omit the bookings display entirely or replace it with
a neutral placeholder (e.g., hide the sentence or show "Booking info
unavailable"). Update the component to accept bookings via props or call the
appropriate data layer, and ensure any conditional rendering handles
loading/error states to avoid showing stale hardcoded numbers.

Comment thread database/event.model.ts
Comment on lines +117 to 119
if (event.isModified("title") || event.isNew) {
event.slug = generateSlug(event.title);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Slug generation lacks collision handling.

If two events have the same title, they'll generate identical slugs. Since slug has unique: true, the second save will fail with a MongoDB duplicate key error. Consider appending a unique suffix (e.g., timestamp or random string) or implementing retry logic.

🔎 Proposed fix example
   // Generate slug only if title changed or document is new
   if (event.isModified("title") || event.isNew) {
-    event.slug = generateSlug(event.title);
+    event.slug = generateSlug(event.title) + "-" + Date.now().toString(36);
   }

Alternatively, implement a more robust approach with collision detection in a utility function.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (event.isModified("title") || event.isNew) {
event.slug = generateSlug(event.title);
}
if (event.isModified("title") || event.isNew) {
event.slug = generateSlug(event.title) + "-" + Date.now().toString(36);
}
🤖 Prompt for AI Agents
In database/event.model.ts around lines 117–119, the pre-save slug assignment
generates identical slugs for identical titles which will violate the schema's
unique constraint; change the hook to an async function that, when title is
new/modified, generates a base slug and then checks the collection for
collisions (e.g., using this.constructor.findOne for that slug) and if found
append a deterministic or random suffix (timestamp, counter, or short random
string) or iterate with a retry counter until a unique slug is produced, then
assign that final unique slug to event.slug before continuing the save.

Comment thread database/event.model.ts
Comment on lines +143 to 150
// Helper function to normalize date to ISO format
function normalizeDate(dateString: string): string {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
throw new Error("Invalid date format");
}
return date.toISOString().split("T")[0]; // Return YYYY-MM-DD format
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

normalizeDate throws raw errors that will surface as 500s.

When the date format is invalid, this throws a generic Error. Since this runs in a pre-save hook, it will cause an unhandled exception rather than a proper Mongoose validation error.

🔎 Consider using Mongoose validation instead

Move date validation to a schema validator so Mongoose handles error formatting:

date: {
  type: String,
  required: [true, "Date is required"],
  validate: {
    validator: (v: string) => !isNaN(new Date(v).getTime()),
    message: "Invalid date format",
  },
  set: (v: string) => new Date(v).toISOString().split("T")[0],
},
🤖 Prompt for AI Agents
In database/event.model.ts around lines 143-150, the helper normalizeDate
currently throws a raw Error on invalid input which will bubble up as a 500 in
the pre-save hook; replace this by moving validation into the Mongoose schema:
declare the date field with required true, add a validate.validator that checks
!isNaN(new Date(v).getTime()) and a validate.message "Invalid date format", and
use a set function on the schema field to normalize the stored value to
YYYY-MM-DD (using new Date(v).toISOString().split("T")[0]) so Mongoose returns a
validation error instead of an unhandled exception.

Comment thread database/event.model.ts
Comment on lines +152 to 182
// Helper function to normalize time format
function normalizeTime(timeString: string): string {
// Handle various time formats and convert to HH:MM (24-hour format)
const timeRegex = /^(\d{1,2}):(\d{2})(\s*(AM|PM))?$/i;
const match = timeString.trim().match(timeRegex);

if (!match) {
throw new Error("Invalid time format. Use HH:MM or HH:MM AM/PM");
}

let hours = parseInt(match[1]);
const minutes = match[2];
const period = match[4]?.toUpperCase();

if (period) {
// Convert 12-hour to 24-hour format
if (period === "PM" && hours !== 12) hours += 12;
if (period === "AM" && hours === 12) hours = 0;
}
return date;

if (
hours < 0 ||
hours > 23 ||
parseInt(minutes) < 0 ||
parseInt(minutes) > 59
) {
throw new Error("Invalid time values");
}

return `${hours.toString().padStart(2, "0")}:${minutes}`;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

normalizeTime throws raw errors and has edge case issues.

Similar to normalizeDate, throwing in a pre-save hook causes unhandled exceptions. Additionally, the regex doesn't handle formats like "9:00 AM" (single-digit hour without leading zero) correctly in all cases.

🔎 Proposed approach

Consider using a Mongoose custom setter with validation, or wrap the normalization in a try-catch within the pre-save hook and use next(error) with a Mongoose ValidationError.

🤖 Prompt for AI Agents
In database/event.model.ts around lines 152-182, the normalizeTime helper
currently throws raw Error and can cause unhandled exceptions; update it to use
a strict regex that tolerates optional surrounding whitespace and single-digit
hours (e.g. /^\s*(\d{1,2}):(\d{2})\s*(AM|PM)?\s*$/i), validate ranges, and
instead of throwing raw Error create and throw a mongoose.Error.ValidationError
(with a ValidationError-like message for the time field) so Mongoose can surface
it properly; then in the model pre-save hook wrap the call to normalizeTime in
try/catch and call next(err) with that ValidationError (or attach the normalized
value and continue) to avoid unhandled exceptions.

Comment thread package.json
Comment on lines +6 to +7
"dev": "next dev --webpack",
"build": "next build --webpack",
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -type f \( -name "next.config.*" -o -name ".next" \) ! -path "./node_modules/*" | head -20

Repository: Preiz68/dev-events

Length of output: 78


🏁 Script executed:

cat -n ./next.config.ts

Repository: Preiz68/dev-events

Length of output: 426


Remove --webpack flag; no custom webpack configuration exists

The next.config.ts contains no custom webpack configuration. Turbopack is now the default bundler for all new Next.js projects and delivers 2–5× faster production builds and up to 10× faster Fast Refresh. The --webpack flag is only needed for apps with custom webpack setups. Since the project has none, remove the flag from both dev and build scripts to benefit from Turbopack's performance improvements.

🤖 Prompt for AI Agents
In package.json around lines 6 to 7, the dev and build scripts include the
obsolete "--webpack" flag even though next.config.ts has no custom webpack
config; remove the "--webpack" flag from both "dev" and "build" script entries
so Next.js uses the default Turbopack bundler and benefits from its faster
builds and Fast Refresh.

@Preiz68 Preiz68 merged commit 0b58d65 into main Dec 22, 2025
1 check passed
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