implement api routes, server actions and caching, and the event detai…#3
Conversation
WalkthroughThis 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–25 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
|
Note Docstrings generation - SUCCESS |
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`
There was a problem hiding this comment.
Actionable comments posted: 12
🧹 Nitpick comments (12)
components/BookEvent.tsx (1)
25-31: Add form validation and required attribute.The email input lacks a
requiredattribute 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
tagdirectly 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.fromEntrieswon'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 thatCLOUDINARY_URLor 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
slugfield already hasunique: trueat line 33, which creates an implicit unique index. The commented-out explicit index declaration is unnecessary and can be removed.
3-21:IEvent extends Documentincludes conflicting type definitions.When extending
Document, Mongoose already provides_id,createdAt,updatedAt, etc. Explicitly definingcreatedAtandupdatedAtin 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 usenext()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 callingnext()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
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (12)
app/api/events/[slug]/route.tsapp/api/events/route.tsapp/event/[slug]/page.tsxapp/page.tsxcomponents/BookEvent.tsxcomponents/EventCard.tsxcomponents/EventDetails.tsxdatabase/booking.model.tsdatabase/event.model.tslib/actions/event.actions.tsnext.config.tspackage.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 newapp/event/[slug]/page.tsxstructure 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
eventsexistence 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 forBASE_URLavailability at runtime.
NEXT_PUBLIC_BASE_URLis 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.
| let tags = JSON.parse(formData.get("tags") as string); | ||
| let agenda = JSON.parse(formData.get("agenda") as string); |
There was a problem hiding this comment.
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.
| if (!events) | ||
| return NextResponse.json({ message: "No event found" }, { status: 404 }); |
There was a problem hiding this comment.
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.
| 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.
| }: { | ||
| params: Promise<{ slug: string }>; | ||
| }) => { | ||
| const slug = params.then((p) => p.slug); |
There was a problem hiding this comment.
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.
| 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(); |
There was a problem hiding this comment.
Add error handling and validation for data fetching.
The fetch call lacks critical error handling:
BASE_URLmight be undefined if the environment variable is not set- No check for
response.okbefore parsing JSON - 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.
| 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.
| const handleSubmit = (e: React.FormEvent) => { | ||
| e.preventDefault(); | ||
|
|
||
| setTimeout(() => { | ||
| setSubmitted(true); | ||
| }, 1000); | ||
| }; |
There was a problem hiding this comment.
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.
| 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; |
There was a problem hiding this comment.
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.
| if (event.isModified("title") || event.isNew) { | ||
| event.slug = generateSlug(event.title); | ||
| } |
There was a problem hiding this comment.
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.
| 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.
| // 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 | ||
| } |
There was a problem hiding this comment.
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.
| // 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}`; | ||
| } |
There was a problem hiding this comment.
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.
| "dev": "next dev --webpack", | ||
| "build": "next build --webpack", |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f \( -name "next.config.*" -o -name ".next" \) ! -path "./node_modules/*" | head -20Repository: Preiz68/dev-events
Length of output: 78
🏁 Script executed:
cat -n ./next.config.tsRepository: 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.
…ls page
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.