Skip to content

implement api routes, server actions and caching, and the event details#4

Merged
modichika merged 2 commits into
mainfrom
feature/add-api-routes
Dec 3, 2025
Merged

implement api routes, server actions and caching, and the event details#4
modichika merged 2 commits into
mainfrom
feature/add-api-routes

Conversation

@modichika
Copy link
Copy Markdown
Owner

@modichika modichika commented Dec 3, 2025

…ls page

Summary by CodeRabbit

  • New Features

    • Create and publish events with image uploads
    • Browse and discover events (server-fetched) and view comprehensive event details with agenda, organizer info, and a booking card
    • Book events via a simple email form; similar events suggested on event pages
  • Bug Fixes / Improvements

    • Improved validation and consistent error responses for event APIs
  • Chores

    • Integrated remote image hosting support and enabled component caching

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 3, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Adds API endpoints for listing, creating, and retrieving events; a detailed event page and booking component; Cloudinary image upload integration; server action to find similar events; model hook and index changes; minor config and logging updates.

Changes

Cohort / File(s) Change Summary
API - Events collection & item
app/api/events/route.ts, app/api/events/[slug]/route.ts
Adds GET (list events) and POST (create event with Cloudinary streaming upload) handlers; adds GET /api/events/[slug] with slug sanitization/validation, DB connect, 404/400/503/500 error responses, and consistent JSON shapes.
Pages
app/page.tsx, app/events/[slug]/page.tsx
Converts homepage to an async server component that fetches events from API with caching; adds event detail page that fetches event by slug, calls server action for similar events, and composes booking/agenda/tags UI.
Components
components/BookEvent.tsx, components/EventCard.tsx
Adds BookEvent client component (email form + submitted state). Updates EventCard id attribute from "event-card" to "events-card".
Database models
database/event.model.ts, database/booking.model.ts
Converts event pre-save hook to async style and comments out slug unique index; adjusts booking pre-save error message text and corrects comment wording.
Server actions & DB util
lib/actions/event.action.ts, lib/mongodb.ts
Adds getSimilarEventsBySlug(slug) server-side function querying similar events by category/tags; adds log statements to connectDB indicating reused vs new connection.
Config & deps
next.config.ts, package.json
Migrates Next.js config to ES module with NextConfig typing, enables cacheComponents, adds Cloudinary remote image pattern; adds cloudinary@^2.8.0 dependency.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant Browser
participant NextAPI as Next.js API Route
participant Cloudinary
participant MongoDB
Browser->>NextAPI: POST /api/events (formData + image)
Note right of NextAPI: connectDB() called
NextAPI->>MongoDB: ensure connection (connectDB)
NextAPI->>Cloudinary: stream upload(image file)
Cloudinary-->>NextAPI: upload URL
NextAPI->>MongoDB: create Event document (with image URL)
MongoDB-->>NextAPI: created event
NextAPI-->>Browser: 201 Created with event JSON

mermaid
sequenceDiagram
autonumber
participant Browser
participant EventPage as Next.js Page
participant ServerAction as getSimilarEventsBySlug
participant MongoDB
Browser->>EventPage: Request /events/[slug]
EventPage->>ServerAction: getSimilarEventsBySlug(slug)
ServerAction->>MongoDB: query current + similar events (exclude current)
MongoDB-->>ServerAction: similar events
ServerAction-->>EventPage: similar events
EventPage-->>Browser: Rendered page with event + similar events

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay special attention to:
    • Slug uniqueness change and its impact (database/event.model.ts) — ensure intended behavior and migrations.
    • Cloudinary streaming upload flow and error handling in app/api/events/route.ts.
    • Validation and status-coding consistency in app/api/events/[slug]/route.ts.
    • getSimilarEventsBySlug query correctness and potential performance (indexes).
    • Changes to Next.js config affecting caching and remote images.

Possibly related PRs

  • Implement database models #2 — touches the same models and DB connection code (database/event.model.ts, database/booking.model.ts, lib/mongodb.ts); likely overlaps schema/connection decisions.

Poem

🐰 I hopped through code, a carrot in tow,
New routes and pages begin to grow.
Cloudinary gleams, Mongo hums along,
Bookings and tags join the rabbit song.
Hop—fetch—deploy—celebrate with a hop! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% 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 partially aligns with the changeset—it mentions API routes, server actions, and event details, which are present. However, it misses critical components like the caching strategy, config changes for Cloudinary, and the booking component that are substantial parts of the PR.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abb442b and 505b8b5.

📒 Files selected for processing (3)
  • app/api/events/[slug]/route.ts (1 hunks)
  • app/api/events/route.ts (1 hunks)
  • components/BookEvent.tsx (1 hunks)

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

@modichika
Copy link
Copy Markdown
Owner Author

@CodeRabbit give me the walkthrough and the sequence diagram

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 3, 2025

Note

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 3, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get PR details and list of changed files
gh pr view 4 --json files,title,body | jq -r '.files[].path' | sort

Length of output: 308


🏁 Script executed:

#!/bin/bash
# Get more details about the PR
gh pr diff 4 --patch | head -200

Length of output: 6123


🏁 Script executed:

#!/bin/bash
# Look for API routes and server actions
fd -e ts -e tsx . --exec echo "=== {} ===" \; --exec cat {}

Length of output: 45933


@modichika Here's a comprehensive walkthrough and sequence diagrams for PR #4:

🎯 Walkthrough

This PR implements a complete event management system with the following architecture:

1. API Routes

/api/events (GET): Fetches all events from MongoDB

  • Connects to database
  • Returns sorted events (newest first)
  • Used by homepage

/api/events (POST): Creates a new event

  • Accepts FormData with event details
  • Uploads image to Cloudinary
  • Parses tags and agenda from JSON
  • Saves event to MongoDB with auto-generated slug

/api/events/[slug] (GET): Fetches single event by slug

  • Validates and sanitizes slug parameter
  • Returns event details or 404

2. Server Actions

getSimilarEventsBySlug: Finds related events

  • Matches by category or tags
  • Excludes current event
  • Returns up to 3 similar events

3. Pages

Homepage (app/page.tsx):

  • Uses Next.js 15 caching with 'use cache' and cacheLife('hours')
  • Fetches events from API
  • Displays event cards

Event Details (app/events/[slug]/page.tsx):

  • Fetches event by slug
  • Shows comprehensive event information
  • Displays booking form
  • Shows similar events using server action

4. Database Models

Event Model:

  • Validates all fields
  • Auto-generates slug from title
  • Normalizes date/time formats
  • Enforces constraints (mode: online/offline/hybrid)

Booking Model:

  • References Event
  • Validates email format
  • Ensures unique booking per event per email

5. Infrastructure

  • MongoDB connection with global caching
  • Cloudinary configuration for image hosting
  • TypeScript throughout

📊 Sequence Diagrams

Flow 1: Browse Events (Homepage)

sequenceDiagram
    actor User
    participant Page as Homepage<br/>(app/page.tsx)
    participant API as /api/events
    participant DB as MongoDB
    participant Cache as Next.js Cache

    User->>Page: Visit homepage
    Page->>Cache: Check cache
    alt Cache Hit
        Cache-->>Page: Return cached events
    else Cache Miss
        Page->>API: GET /api/events
        API->>DB: connectDB()
        DB-->>API: Connection established
        API->>DB: Event.find().sort()
        DB-->>API: Return events array
        API-->>Page: { events: [...] }
        Page->>Cache: Store for 1 hour
    end
    Page->>User: Display event cards
Loading

Flow 2: View Event Details

sequenceDiagram
    actor User
    participant Page as Event Details Page
    participant API as /api/events/[slug]
    participant Action as getSimilarEventsBySlug
    participant DB as MongoDB

    User->>Page: Click event card
    Page->>API: GET /api/events/{slug}
    API->>DB: connectDB()
    API->>API: Validate & sanitize slug
    API->>DB: Event.findOne({slug})
    DB-->>API: Return event or null
    
    alt Event Found
        API-->>Page: { event: {...} }
        Page->>Action: getSimilarEventsBySlug(slug)
        Action->>DB: Find current event
        Action->>DB: Query by category/tags
        DB-->>Action: Return similar events
        Action-->>Page: [similarEvents]
        Page->>User: Display event details + similar events
    else Event Not Found
        API-->>Page: 404 Not Found
        Page->>User: Show 404 page
    end
Loading

Flow 3: Book Event

sequenceDiagram
    actor User
    participant Form as BookEvent Component
    participant State as React State

    User->>Form: Enter email
    User->>Form: Click Submit
    Form->>Form: handleSubmit(e)
    Form->>State: Simulate booking (setTimeout)
    State->>State: setSubmitted(true)
    Form->>User: Show "Thank you" message
    
    Note over Form,State: Current implementation is client-side only<br/>Backend booking API not yet implemented
Loading

Flow 4: Create Event

sequenceDiagram
    actor Admin
    participant API as /api/events
    participant Cloudinary
    participant DB as MongoDB
    participant Model as Event Model

    Admin->>API: POST /api/events (FormData)
    API->>API: Parse formData
    API->>API: Extract image file
    
    API->>Cloudinary: Upload image
    Cloudinary-->>API: Return secure_url
    
    API->>API: Parse tags & agenda JSON
    API->>Model: Event.create({...})
    
    Model->>Model: Pre-save hook
    Model->>Model: Generate slug from title
    Model->>Model: Normalize date & time
    
    Model->>DB: Save document
    DB-->>Model: Return created event
    Model-->>API: Return event
    API-->>Admin: { event: {...}, status: 201 }
Loading

Flow 5: Database Connection

sequenceDiagram
    participant API as API Route
    participant Connect as connectDB()
    participant Cache as Global Cache
    participant Mongoose
    participant MongoDB

    API->>Connect: await connectDB()
    Connect->>Cache: Check cached.conn
    
    alt Connection Exists
        Cache-->>Connect: Return cached connection
        Connect-->>API: Return mongoose instance
    else No Connection
        Connect->>Cache: Check cached.promise
        alt Promise Exists
            Cache-->>Connect: Await existing promise
        else Create New
            Connect->>Mongoose: mongoose.connect(URI)
            Mongoose->>MongoDB: Establish connection
            MongoDB-->>Mongoose: Connected
            Mongoose-->>Connect: Return instance
            Connect->>Cache: Store connection
        end
        Connect-->>API: Return mongoose instance
    end
Loading

🔑 Key Features

  1. Caching Strategy: Homepage uses Next.js 15 partial prerendering with 1-hour cache
  2. Image Hosting: Cloudinary integration for scalable image storage
  3. Data Validation: Comprehensive validation at model level
  4. Security: Slug sanitization, email validation, input validation
  5. Performance: MongoDB connection pooling with global caching
  6. Similar Events: Smart recommendations based on tags and category

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

* #4 (comment)

The following files were modified:

* `app/api/events/[slug]/route.ts`
* `app/api/events/route.ts`
* `database/event.model.ts`
* `lib/mongodb.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: 11

🧹 Nitpick comments (13)
components/BookEvent.tsx (2)

9-11: Add loading state to provide user feedback during submission.

Users have no indication that anything is happening during the 1-second wait (or during an actual API call). Consider adding an isLoading state:

         const [email, setEmail] = useState('');
         const [submitted, setSubmitted] = useState(false);
+        const [isLoading, setIsLoading] = useState(false);

Then use it to disable the button and show feedback during submission.


26-36: Add required attribute and disable button during submission.

The email input should be marked as required for proper HTML validation, and the button should be disabled while submitting to prevent duplicate submissions:

                         <input type={"email"}
                                value={email}
                                onChange={(e) => setEmail(e.target.value)}
                                id={"email"}
                                placeholder={"Enter your email address"}
+                               required
                         />
                     </div>
-                    <button type={"submit"} className={"button-submit"}>Submit</button>
+                    <button type={"submit"} className={"button-submit"} disabled={isLoading}>
+                        {isLoading ? 'Submitting...' : 'Submit'}
+                    </button>
database/event.model.ts (1)

180-181: Clarify or remove the commented-out index.

The explicit index on line 181 is now redundant because unique: true on the schema field (line 33) already creates a unique index. The comment "for better performance" is misleading since it's disabled.

Either remove the dead code entirely or add a clarifying comment:

-// Create unique index on slug for better performance
-//EventSchema.index({ slug: 1 }, { unique: true });
+// Note: Unique index on slug is created automatically via schema-level `unique: true`
lib/mongodb.ts (1)

33-33: Consider removing or gating debug logs for production.

These console.log statements will execute on every request. While useful during development, they may clutter production logs. Consider using a logging library with configurable log levels or wrapping these in a development-only check:

 if (cached.conn) {
-    console.log("🟢 USING EXISTING CONNECTION");
+    if (process.env.NODE_ENV === 'development') {
+        console.log("🟢 USING EXISTING CONNECTION");
+    }
     return cached.conn;
 }

Also applies to: 39-39

lib/actions/event.action.ts (2)

19-21: Remove debug logs before production.

Multiple console.log statements with debug prefixes will clutter production logs. Consider removing them or using a conditional check for development environment.

Also applies to: 35-35, 40-40


6-17: Consider sanitizing the slug input.

The API route at app/api/events/[slug]/route.ts sanitizes slugs (trimming, lowercasing, regex validation), but this server action uses the slug directly. For consistency and defense-in-depth, consider applying similar sanitization here.

 export const getSimilarEventsBySlug = async (slug: string) => {
     try {
         await connectDB();
+        
+        // Sanitize slug
+        const sanitizedSlug = slug.trim().toLowerCase();

         // 1. Find the current event
-        const event = await Event.findOne({ slug });
+        const event = await Event.findOne({ slug: sanitizedSlug });
app/events/[slug]/page.tsx (3)

107-109: Use a unique identifier for the React key.

Using similarEvent.title as the key could cause issues if two events have the same title. Use _id from the MongoDB document instead for guaranteed uniqueness.

-{similarEvents.length > 0 && similarEvents.map((similarEvent: IEvent) => (
-    <EventCard key={String(similarEvent.title)} {...similarEvent} />
+{similarEvents.length > 0 && similarEvents.map((similarEvent: IEvent) => (
+    <EventCard key={String((similarEvent as any)._id || similarEvent.slug)} {...similarEvent} />
 ))}

51-51: Remove debug console.log.

This debug log will execute in production. Remove it before merging.

-    console.log({similarEvents});

19-20: Potential duplicate keys in lists.

Agenda items and tags use the item/tag value as the React key. If duplicates exist in these arrays, React will log warnings and may not render correctly. Consider using the index as a fallback:

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

Also applies to: 29-30

app/api/events/route.ts (2)

14-18: Unnecessary try-catch around Object.fromEntries.

Object.fromEntries(formData.entries()) does not throw for "invalid JSON" - it simply converts FormData entries to an object. This try-catch block will never catch the intended error. If you need JSON body parsing, use req.json() instead.

-        let event;
-        
-        try {
-            event = Object.fromEntries(formData.entries());
-        } catch (e) {
-            return NextResponse.json({message: 'Invalid JSON data format'}, {status: 400});
-        }
+        const event = Object.fromEntries(formData.entries());

42-46: Consider validating required event fields before database insertion.

The event is created without validating that required fields (like title, slug, description) are present. While Mongoose schema validation may catch this, returning a 400 with specific missing field information provides a better API experience.

app/page.tsx (1)

11-12: Tighten typing for events and use a stable key

Two small improvements here:

  1. Type the JSON response so events is known to be IEvent[] and you get compile-time safety if the API shape drifts:
-    const {events} = await response.json();
+    const { events }: { events: IEvent[] } = await response.json();
  1. Use slug as the React key instead of title for better stability and uniqueness:
-                    {events && events.length > 0 && events.map((event: IEvent) => (
-                        <li key={event.title} className={"list-none"}>
+                    {events && events.length > 0 && events.map((event: IEvent) => (
+                        <li key={event.slug} className={"list-none"}>

Also applies to: 23-24

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

16-27: Prefer RouteContext over a custom RouteParams type

Next.js now exposes a global RouteContext helper type for Route Handlers so you can get strongly typed params without defining your own context shape. (nextjs.org)

You can simplify and align with the docs like this:

-import { NextRequest, NextResponse } from 'next/server';
+import { NextRequest, NextResponse } from 'next/server';
 
-// Define params type for Next.js dynamic route
-type RouteParams = {
-  params: Promise<{
-    slug: string;
-  }>;
-}
-
 export async function GET(
-  req: NextRequest,
-  { params }: RouteParams
+  req: NextRequest,
+  ctx: RouteContext<'/api/events/[slug]'>
 ): Promise<NextResponse> {
   try {
-      // Connect to database
-      await connectDB();
-
-    // Await and extract slug for params
-    const { slug } = await params;
+    // Await and extract slug from context params
+    const { slug } = await ctx.params;
+
+    // Connect to database
+    await connectDB();

(You can keep or adjust the connect order as you prefer.)

This removes a custom type and stays in sync with Next’s generated route types.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f317f28 and abb442b.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • public/images/event2.png is excluded by !**/*.png
📒 Files selected for processing (12)
  • app/api/events/[slug]/route.ts (1 hunks)
  • app/api/events/route.ts (1 hunks)
  • app/events/[slug]/page.tsx (1 hunks)
  • app/page.tsx (2 hunks)
  • components/BookEvent.tsx (1 hunks)
  • components/EventCard.tsx (1 hunks)
  • database/booking.model.ts (2 hunks)
  • database/event.model.ts (2 hunks)
  • lib/actions/event.action.ts (1 hunks)
  • lib/mongodb.ts (1 hunks)
  • next.config.ts (2 hunks)
  • package.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
app/api/events/route.ts (2)
app/api/events/[slug]/route.ts (1)
  • GET (16-101)
lib/constants.ts (1)
  • events (11-108)
app/events/[slug]/page.tsx (2)
database/event.model.ts (1)
  • IEvent (4-21)
lib/actions/event.action.ts (1)
  • getSimilarEventsBySlug (6-43)
app/api/events/[slug]/route.ts (2)
app/api/events/route.ts (1)
  • GET (56-67)
database/event.model.ts (1)
  • IEvent (4-21)
app/page.tsx (2)
lib/constants.ts (1)
  • events (11-108)
database/event.model.ts (1)
  • IEvent (4-21)
🔇 Additional comments (7)
database/booking.model.ts (2)

53-56: Grammar correction looks good.

The error message fix from "events ID" to "event ID" improves clarity.


66-73: Index definitions are well-structured.

The compound index on (eventId, createdAt) optimizes common query patterns, and the unique constraint on (eventId, email) properly enforces the one-booking-per-email-per-event business rule.

database/event.model.ts (1)

115-131: Async pre-save hook migration is correct for Mongoose 9.x.

The migration from callback-style (next) to async functions is the modern approach in Mongoose. The hook will properly await and Mongoose handles errors thrown from async hooks.

next.config.ts (3)

4-9: Cloudinary remote pattern configuration looks good.

The remote pattern correctly allows loading images from res.cloudinary.com over HTTPS, which aligns with the Cloudinary integration added in this PR.


1-2: LGTM on the ES module migration.

The migration from CommonJS to ES module syntax with proper TypeScript typing is correct for Next.js 15.

Also applies to: 26-26


3-3: The cacheComponents option is valid in Next.js 15 and the configuration is correct.

cacheComponents: true is a recognized Next.js 15 configuration option that enables the new Cache Components model, including runtime component caching, partial prerendering (PPR), and related APIs like the use cache directive, cacheLife, and cacheTag. No changes are needed.

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

47-47: Hardcoded booking count.

The bookings value is hardcoded to 10. If this is intentional placeholder behavior, consider adding a TODO comment. Otherwise, this should be fetched from the API or database.

Comment on lines +77 to +99
} catch (error) {
console.error('Error fetching events by slug:', error);

// Handle database connection errors
if (error instanceof Error && error.message.includes('connection')) {
return NextResponse.json(
{
message: 'Database connection error',
success: false,
},
{ status: 503 }
);
}

// Handle unexpected errors
return NextResponse.json(
{
message: 'Failed to fetch events',
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
},
{ status: 500 }
);
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

Avoid returning internal error details in API responses

The 500-path currently exposes error.message back to the client:

return NextResponse.json(
  {
    message: 'Failed to fetch events',
    success: false,
    error: error instanceof Error ? error.message : 'Unknown error',
  },
  { status: 500 }
);

That can leak internal implementation details or environment info (DB errors, stack fragments) over a public API. Better to log the full error on the server and return a generic message to clients.

A safer variant:

   } catch (error) {
     console.error('Error fetching events by slug:', error);
@@
-    // Handle unexpected errors
-    return NextResponse.json(
-      {
-        message: 'Failed to fetch events',
-        success: false,
-        error: error instanceof Error ? error.message : 'Unknown error',
-      },
-      { status: 500 }
-    );
+    // Handle unexpected errors (don't leak internal details to clients)
+    return NextResponse.json(
+      {
+        message: 'Failed to fetch event',
+        success: false,
+      },
+      { status: 500 }
+    );
   }
 }
🤖 Prompt for AI Agents
In app/api/events/[slug]/route.ts around lines 77 to 99, the 500 error response
currently returns error.message to the client which may leak internal details;
instead, log the full error on the server (console.error or application logger)
and change the JSON response to return a generic failure message and success:
false without including error.message or stack; keep the 503 path for connection
errors as-is but ensure only non-sensitive, generic text is sent to clients on
the 500 path.

Comment thread app/api/events/route.ts
@@ -0,0 +1,67 @@
import {NextRequest, NextResponse} from "next/server";
import { v2 as cloudinary } from 'cloudinary';
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:

#!/bin/bash
# Check if Cloudinary is configured elsewhere in the codebase
rg -n "cloudinary.config" --type ts
rg -n "CLOUDINARY" --type ts

Repository: modichika/dev-events-nextjs16

Length of output: 55


🏁 Script executed:

# Read the file to see context around the import and lines 32-37
wc -l app/api/events/route.ts

Repository: modichika/dev-events-nextjs16

Length of output: 99


🏁 Script executed:

# Read the entire file (if reasonable size) or specific sections
cat -n app/api/events/route.ts

Repository: modichika/dev-events-nextjs16

Length of output: 2732


🏁 Script executed:

# Check if there's any Cloudinary config file or environment setup
fd -type f -name "*.ts" -o -name "*.js" -o -name "*.env*" | head -20

Repository: modichika/dev-events-nextjs16

Length of output: 243


🏁 Script executed:

# Search more broadly for CLOUDINARY references
rg "CLOUDINARY" --type ts --type js -A 2

Repository: modichika/dev-events-nextjs16

Length of output: 55


🏁 Script executed:

# Check for any configuration initialization patterns
rg "config\(" --type ts -B 2 -A 2 | head -40

Repository: modichika/dev-events-nextjs16

Length of output: 55


🏁 Script executed:

# Check project root for any configuration or setup files
ls -la | grep -E "(cloudinary|config|env)"

Repository: modichika/dev-events-nextjs16

Length of output: 342


🏁 Script executed:

# Check if there's a setup file or initialization
fd "middleware|setup|init|config" --extension ts --extension js

Repository: modichika/dev-events-nextjs16

Length of output: 87


Cloudinary configuration missing — add explicit setup or validation.

The Cloudinary SDK at line 2 is imported but never explicitly configured. At lines 32-37, cloudinary.uploader.upload_stream() is called directly without verifying configuration, which will fail silently or with unclear errors if environment variables (CLOUDINARY_URL or individual credentials) are not set. Add explicit cloudinary.config() call with validation or environment variable checks to fail fast with a clear error message.

🤖 Prompt for AI Agents
In app/api/events/route.ts around line 2, the Cloudinary SDK is imported but not
configured; before any call to cloudinary.uploader.upload_stream (around lines
32–37) validate required env vars (CLOUDINARY_URL or CLOUDINARY_CLOUD_NAME,
CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET), call cloudinary.config(...) with
those values, and if any are missing throw or log a clear, early error so the
route fails fast with an actionable message; ensure the config step runs once at
module init and not per-request.

Comment thread app/api/events/route.ts Outdated
Comment thread app/api/events/route.ts Outdated
import BookEvent from "@/components/BookEvent";
import {getSimilarEventsBySlug} from "@/lib/actions/event.action";
import EventCard from "@/components/EventCard";
const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
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 | 🟡 Minor

Add fallback or validation for BASE_URL.

If NEXT_PUBLIC_BASE_URL is not set, the fetch call at line 38 will produce an invalid URL like undefined/api/events/..., causing a runtime error. Consider adding a fallback or throwing an explicit error during build/startup.

-const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
+const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL;
+if (!BASE_URL) {
+    throw new Error('NEXT_PUBLIC_BASE_URL environment variable is not set');
+}

Alternatively, for server components, you could use relative URLs since the fetch happens on the server:

-const request = await fetch(`${BASE_URL}/api/events/${slug}`);
+const request = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL || ''}/api/events/${slug}`);

Also applies to: 38-38

🤖 Prompt for AI Agents
In app/events/[slug]/page.tsx around line 7 (and usage at line 38), BASE_URL is
read from process.env.NEXT_PUBLIC_BASE_URL without validation which can create
an invalid fetch target (e.g. "undefined/api/..."); add a validation/fallback:
either set BASE_URL = process.env.NEXT_PUBLIC_BASE_URL ??
process.env.NEXT_PUBLIC_VERCEL_URL ?? '' and if empty throw a clear Error during
startup/build, or (preferred for server components) replace the absolute URL
construction with a relative path (e.g. start the fetch with '/api/events/...'
so no env var is required); ensure any error path logs a clear message and avoid
creating URLs when BASE_URL is falsy.

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

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

@coderabbitai coderabbitai Bot Dec 3, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Booking data is not persisted — email is captured but never sent to an API.

The handleSubmit function only simulates a delay and updates local state. The email value is never submitted to the server, so no booking is actually created. This renders the form non-functional.

Consider calling your booking API or server action:

-        const handleSubmit = (e: React.FormEvent) => {
+        const handleSubmit = async (e: React.FormEvent) => {
               e.preventDefault();
-
-              setTimeout(() => {
-                  setSubmitted(true);
-              }, 1000)
+              // TODO: Replace with actual eventId prop
+              const response = await fetch('/api/bookings', {
+                  method: 'POST',
+                  headers: { 'Content-Type': 'application/json' },
+                  body: JSON.stringify({ email, eventId }),
+              });
+              if (response.ok) {
+                  setSubmitted(true);
+              }
         }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In components/BookEvent.tsx around lines 13 to 19, handleSubmit currently only
simulates a delay and sets local state, so the captured email is never sent to
the backend; replace the timeout with an async submission flow that calls your
booking API or server action with the email (and any other required fields),
await the response, handle success by setting submitted=true and
clearing/loading state, and handle errors by showing user feedback and logging;
also disable repeated submissions while the request is in flight and ensure
proper error handling for non-2xx responses.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@CodeRabbit in this issue the eventID is not found typescript is suggesting

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how BookEvent is used in the event details page
cat app/events/[slug]/page.tsx | grep -A5 -B5 "BookEvent"

# Also check the full BookEvent component to understand current structure
cat components/BookEvent.tsx

# Check if there's an event ID field in the event model
cat database/event.model.ts | grep -i "_id\|id:"

Length of output: 2174


@modichika You're right! The eventId isn't available because BookEvent doesn't receive any props. Here's the corrected solution:

1. Update BookEvent component to accept the event ID:

'use client';

import React, {useState} from "react";

interface BookEventProps {
    eventId: string;
}

const BookEvent = ({ eventId }: BookEventProps) => {
    const [email, setEmail] = useState('');
    const [submitted, setSubmitted] = useState(false);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState<string | null>(null);

    const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault();
        setLoading(true);
        setError(null);
        
        try {
            const response = await fetch('/api/bookings', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ email, eventId }),
            });
            
            if (response.ok) {
                setSubmitted(true);
            } else {
                const data = await response.json();
                setError(data.message || 'Failed to book event');
            }
        } catch (err) {
            setError('Network error. Please try again.');
        } finally {
            setLoading(false);
        }
    }

    // ... rest of component

2. Update the parent page to pass the event ID:

In app/events/[slug]/page.tsx, change:

<BookEvent />

to:

<BookEvent eventId={event._id} />

This way TypeScript will be satisfied because eventId is properly typed and passed as a prop!

Comment thread components/EventCard.tsx
const EventCard = ({title,image, slug, location, time, date} : Props) => {
return (
<Link href={`/events/${slug}`} id={"event-card"}>
<Link href={`/events/${slug}`} id={"events-card"}>
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 | 🟡 Minor

Duplicate id attribute across multiple card instances.

Since EventCard is rendered in a list, every card will share the same id="events-card", violating HTML uniqueness requirements for id attributes. This can cause issues with accessibility (screen readers), CSS selectors, and document.getElementById.

Consider using className instead if styling is the intent, or remove the id if it's not needed:

-        <Link href={`/events/${slug}`} id={"events-card"}>
+        <Link href={`/events/${slug}`} className={"event-card"}>
📝 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
<Link href={`/events/${slug}`} id={"events-card"}>
<Link href={`/events/${slug}`} className={"event-card"}>
🤖 Prompt for AI Agents
In components/EventCard.tsx around line 16, the Link element uses a fixed
id="events-card" which will be duplicated for every rendered card; remove the id
or replace it with a className (e.g., className="events-card") or generate a
unique id per item (e.g., using the slug or an index) depending on whether you
need a styling hook or a programmatic unique identifier. Ensure accessibility
and any JS that queries the element are updated to use the new class or unique
id.

Comment thread database/event.model.ts
Comment on lines 118 to 120
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 | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's find the Event model file and understand its schema
find . -type f -name "*.ts" -o -name "*.js" | grep -i event | head -20

Repository: modichika/dev-events-nextjs16

Length of output: 187


🏁 Script executed:

# Search for the Event model schema definition to check for unique constraints on slug
rg -n "slug.*unique|unique.*slug" --type ts -A 2 -B 2

Repository: modichika/dev-events-nextjs16

Length of output: 394


🏁 Script executed:

# Find the generateSlug function implementation
rg -n "generateSlug|function generateSlug" --type ts -A 5

Repository: modichika/dev-events-nextjs16

Length of output: 787


🏁 Script executed:

# Check for any existing slug collision handling
rg -n "slug.*collision|duplicate.*slug|slug.*exist|findOne.*slug" --type ts

Repository: modichika/dev-events-nextjs16

Length of output: 276


🏁 Script executed:

# View the Event schema definition, especially the slug field
sed -n '1,180p' database/event.model.ts | tail -100

Repository: modichika/dev-events-nextjs16

Length of output: 3123


🏁 Script executed:

# Check the full generateSlug function and surrounding context
sed -n '130,145p' database/event.model.ts

Repository: modichika/dev-events-nextjs16

Length of output: 608


🏁 Script executed:

# Search for the slug field definition in the Event schema
rg -n "slug.*:" database/event.model.ts -B 2 -A 2

Repository: modichika/dev-events-nextjs16

Length of output: 549


🏁 Script executed:

# Check what indexes are actually defined on the Event model
rg -n "\.index\(" database/event.model.ts -A 1 -B 1

Repository: modichika/dev-events-nextjs16

Length of output: 289


🏁 Script executed:

# Check if there's any handling for the unique constraint elsewhere
rg -n "unique.*true|{ unique:" database/event.model.ts

Repository: modichika/dev-events-nextjs16

Length of output: 183


Slug collisions will cause save failures for events with duplicate titles.

The slug field has unique: true defined in the schema (line 33), enforcing database-level uniqueness. Since generateSlug is deterministic, two events with identical titles will produce the same slug, violating the unique constraint and causing save failures.

Append a unique suffix to handle collisions:

     if (event.isModified('title') || event.isNew) {
-        event.slug = generateSlug(event.title);
+        const baseSlug = generateSlug(event.title);
+        const existing = await model('Event').findOne({ slug: baseSlug, _id: { $ne: event._id } });
+        event.slug = existing ? `${baseSlug}-${Date.now()}` : baseSlug;
     }
📝 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) {
const baseSlug = generateSlug(event.title);
const existing = await model('Event').findOne({ slug: baseSlug, _id: { $ne: event._id } });
event.slug = existing ? `${baseSlug}-${Date.now()}` : baseSlug;
}
🤖 Prompt for AI Agents
In database/event.model.ts around lines 118 to 120, the pre-save hook sets a
deterministic slug which can collide because slug has unique: true; change the
hook to generate a base slug and then check the collection for existing
documents with that slug (excluding the current document id), and if a collision
exists append a numeric suffix (e.g., -1, -2, ...) incrementing until you find a
slug not present; make the hook async (use event.constructor or the model to
query with findOne or countDocuments) and assign the first-available unique slug
to event.slug before saving.


console.log("🔍 DEBUG: Current Event Found:", event.title);
console.log("🏷️ DEBUG: Tags to match:", event.tags);
console.log("📂 DEBUG: Category to match:", event.category);
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:

# Check for category field in event.model.ts
rg -n "category" database/event.model.ts -A 2 -B 2

Repository: modichika/dev-events-nextjs16

Length of output: 202


🏁 Script executed:

# Get the full IEvent interface and Event schema definition
cat -n database/event.model.ts

Repository: modichika/dev-events-nextjs16

Length of output: 6836


🏁 Script executed:

# Check the actual usage in event.action.ts around lines 21 and 27
sed -n '15,30p' lib/actions/event.action.ts

Repository: modichika/dev-events-nextjs16

Length of output: 749


Remove non-existent category field references.

The IEvent interface and Event schema in database/event.model.ts do not define a category field. Lines 21 and 27 reference event.category, which will always be undefined, causing the category-based matching in the query to fail silently.

Either add category: string to the IEvent interface and Event schema, or remove the category matching logic from lines 21 and 27.

Also remove debug console.log statements on lines 19-21, 35, and 40 before merging.

🤖 Prompt for AI Agents
In lib/actions/event.action.ts around lines 19–40, the code references a
non-existent event.category (lines 21 and 27) and contains several debug
console.log calls (lines 19–21, 35, 40); remove the category-based matching
logic (remove any checks or query predicates that use event.category) or
alternatively add a category: string to the IEvent interface and the Event
schema in database/event.model.ts if category is required — choose one
consistent approach across model, interface, and this action; also delete all
debug console.log statements on lines 19–21, 35, and 40 before merging so no
stray debug output remains.

Comment thread package.json
},
"dependencies": {
"class-variance-authority": "^0.7.1",
"cloudinary": "^2.8.0",
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

🌐 Web query:

cloudinary npm package latest version 2024 2025

💡 Result:

As of Dec 3, 2025:

  • cloudinary (npm package) — latest published version: 2.7.0. [1]
  • cloudinary-core (client-side) — latest published version: 2.14.0. [2]

Note: Cloudinary released a security/SDK update in 2025 and the 2.7.0 release fixes a known vulnerability; upgrading to 2.7.0+ is recommended. [3]

Sources:
[1] npm package page for cloudinary.
[2] npm package page / Snyk for cloudinary-core.
[3] Cloudinary Node SDK docs / security advisory.


Cloudinary version 2.8.0 does not exist—correct this to 2.7.0.

The specified version ^2.8.0 is invalid; the latest published version of the cloudinary npm package is 2.7.0 (as of December 2025). Using a non-existent version will cause npm install to fail or resolve to an unexpected version. Additionally, Cloudinary released a security update in 2025 with version 2.7.0+, making the upgrade necessary.

🤖 Prompt for AI Agents
In package.json around line 13, the dependency version for "cloudinary" is set
to the non-existent "^2.8.0"; update it to the valid published version (e.g.,
"2.7.0" or a caret range like "^2.7.0") to ensure npm install succeeds and picks
up the security-patched release—modify the version string accordingly and run
npm install (or npm ci) to verify dependency resolution.

@modichika modichika merged commit 0b6bb99 into main Dec 3, 2025
1 check was pending
@coderabbitai coderabbitai Bot mentioned this pull request Dec 10, 2025
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.

1 participant